Skip to content

Commit 45cef19

Browse files
authored
Merge branch 'master' into fix-mcp-routing-openai-strict
2 parents be93b6b + ca4689d commit 45cef19

13 files changed

Lines changed: 314 additions & 52 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
- Bugfix: OpenAI Responses tool calls now opt out of strict schema normalization so optional tool parameters remain optional.
66
- Bugfix: MCP tool calls now route to the selected server when multiple servers expose the same tool name.
7+
- Use a JSON-RPC `ping` (instead of `initialize`) for the OAuth auth-discovery probe, so the probe POST is never counted as a real handshake by servers or tests that track requests by method name.
8+
9+
## 0.134.1
10+
11+
- Support optional `clientName` config field for MCP servers to override the OAuth Dynamic Client Registration `client_name` (useful for servers that allowlist clients by name, e.g. Figma).
12+
13+
## 0.134.0
14+
15+
- Support including `AGENTS.md` files from parent directories of each workspace folder via new `includeParentAgentsFiles` config flag (disabled by default), ordered outermost parent first.
16+
- Replace custom stderr-print logger with Logback/SLF4J: timestamps, log levels, chat-id MDC context, third-party noise suppression (root at WARN, `eca` at INFO), and proper cross-thread context propagation in `future*`. #253
17+
- Fix MCP OAuth auto-discovery for servers that only return a 401 + `www-authenticate` challenge when probed with a valid JSON-RPC initialize request (e.g. Figma).
18+
- Mark MCP servers as failed when the initialize handshake returns no result, instead of silently appearing as running.
719

820
## 0.133.6
921

docs/config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@
194194
"maximum": 100,
195195
"default": 75
196196
},
197+
"includeParentAgentsFiles": {
198+
"type": "boolean",
199+
"description": "When true, also include AGENTS.md files from each workspace folder's parent directories, ordered outermost parent first, then the workspace's own AGENTS.md.",
200+
"markdownDescription": "When `true`, also include `AGENTS.md` files from each workspace folder's parent directories, ordered outermost parent first, then the workspace's own `AGENTS.md`.",
201+
"default": false
202+
},
197203
"index": {
198204
"type": "object",
199205
"description": "Indexing configuration for workspace files.",
@@ -1116,6 +1122,11 @@
11161122
"maximum": 65535,
11171123
"description": "Fixed port for the OAuth callback server. Required by providers like Slack that mandate pre-registered redirect URIs. When set, the callback uses HTTPS with a bundled localhost certificate. Register https://localhost:<port>/auth/callback as the redirect URI in your OAuth app.",
11181124
"markdownDescription": "Fixed port for the OAuth callback server. Required by providers like Slack that mandate pre-registered redirect URIs. When set, the callback uses HTTPS with a bundled localhost certificate. Register `https://localhost:<port>/auth/callback` as the redirect URI in your OAuth app."
1125+
},
1126+
"clientName": {
1127+
"type": "string",
1128+
"description": "Override the client_name sent during OAuth Dynamic Client Registration. Required for servers that allowlist clients by name (e.g. Figma). Defaults to 'ECA (Editor Code Assistant)' when not set.",
1129+
"markdownDescription": "Override the `client_name` sent during OAuth Dynamic Client Registration. Required for servers that allowlist clients by name (e.g. Figma). Defaults to `ECA (Editor Code Assistant)` when not set."
11191130
}
11201131
},
11211132
"additionalProperties": false

docs/config/tools.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,26 @@ For MCP servers configuration, use the `mcpServers` config, examples:
111111
}
112112
```
113113

114+
=== "Advanced — DCR client name override"
115+
116+
Some OAuth-protected MCP servers allowlist clients during Dynamic Client
117+
Registration (DCR) by `client_name`. If the server rejects ECA's default
118+
registration with a 403, set `clientName` to a value the server accepts.
119+
120+
```javascript title="~/.config/eca/config.json"
121+
{
122+
"mcpServers": {
123+
"figma": {
124+
"url": "https://mcp.figma.com/mcp",
125+
"clientName": "Claude Code"
126+
}
127+
}
128+
}
129+
```
130+
131+
The DCR attempt, its result and the chosen `client_name` are logged at
132+
`info`/`warn` level so you can verify behavior in the ECA log.
133+
114134
=== "Disabling a MCP"
115135

116136
Set `"disabled": true` to keep the configuration but prevent ECA from starting the server.

docs/features.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ So user can include those contexts in 3 different ways for different purposes:
111111

112112
ECA will always include if found the `AGENTS.md` file as context, searching for both `/project-root/AGENTS.md` and `~/.config/eca/AGENTS.md`, it will recursively check for any `@some-file.md` mention as well.
113113

114+
Set the config flag `includeParentAgentsFiles` to `true` to also include `AGENTS.md` files from each workspace's parent directories (up to the filesystem root). Files are emitted outermost parent first, then descending toward the workspace's own `AGENTS.md`, and duplicates across overlapping workspaces are skipped. The flag is disabled by default.
115+
114116
You can ask ECA to create/update this file via `/init` command.
115117
you can check/debug what goes to final prompt with `/prompt-show` as well.
116118

resources/ECA_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.133.6
1+
0.134.1

resources/prompts/tools/git.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ gh pr create --title "the pr title" --body "$(cat <<'EOF'
3737
[Testing checklist]
3838

3939
🤖 Generated with [eca](https://eca.dev)
40+
41+
Co-Authored-By: eca-agent <git@eca.dev>
4042
EOF
4143
)"
4244
</example>

src/eca/config.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
:completion {:model "openai/gpt-4.1"}
225225
:netrcFile nil
226226
:autoCompactPercentage 75
227+
:includeParentAgentsFiles false
227228
:plugins {"eca" {:source "https://github.com/editor-code-assistant/eca-plugins.git"}}
228229
:remote {:enabled false}
229230
:env "prod"})

src/eca/features/chat.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,7 @@
11851185
:state :running
11861186
:text "Parsing given context"}))
11871187
refined-contexts (concat
1188-
(f.context/agents-file-contexts db)
1188+
(f.context/agents-file-contexts db config)
11891189
(f.context/raw-contexts->refined contexts db))
11901190
{static-rules :static path-scoped-rules :path-scoped} (f.rules/all-rules config (:workspace-folders db) agent full-model)
11911191
all-tools (f.tools/all-tools chat-id agent @db* config)

src/eca/features/context.clj

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,57 @@
5555
nested-results))
5656
[]))))
5757

58+
(defn ^:private ancestor-paths
59+
"Return all ancestor directories of `path` in outermost-first order.
60+
Excludes `path` itself."
61+
[path]
62+
(loop [p (fs/parent path)
63+
acc '()]
64+
(if (nil? p)
65+
(vec acc)
66+
(recur (fs/parent p) (conj acc p)))))
67+
68+
(defn ^:private safe-canonicalize
69+
"Canonicalize `path`, falling back to a non-resolved `fs/path` if the
70+
path does not exist (e.g. a stale workspace folder or test fixture)."
71+
[path]
72+
(try (fs/canonicalize path)
73+
(catch Exception _ (fs/path path))))
74+
5875
(defn agents-file-contexts
5976
"Search for AGENTS.md file both in workspaceRoot and global config dir.
77+
When `:includeParentAgentsFiles` is true in `config`, also include
78+
AGENTS.md files from each workspace's parent directories, ordered
79+
outermost parent first, then the workspace's own AGENTS.md.
6080
Process any found @paths mentions recursively, supporting both relative and absolute paths.
6181
Deduplicates files to avoid reading the same file multiple times."
62-
[db]
82+
[db config]
6383
;; TODO make it customizable by agent
64-
(let [agent-file "AGENTS.md"
65-
local-agent-files (keep (fn [{:keys [uri]}]
66-
(let [agent-file (fs/path (shared/uri->filename uri) agent-file)]
67-
(when (fs/readable? agent-file)
68-
(fs/canonicalize agent-file))))
69-
(:workspace-folders db))
70-
global-agent-file (let [agent-file (fs/path (shared/global-config-dir) agent-file)]
71-
(when (fs/readable? agent-file)
72-
(fs/canonicalize agent-file)))]
73-
(->> (concat local-agent-files
74-
(when global-agent-file [global-agent-file]))
75-
(mapcat #(parse-agents-file (str %))))))
84+
(let [agent-file-name "AGENTS.md"
85+
include-parents? (boolean (:includeParentAgentsFiles config))
86+
readable-agent-file-in (fn [dir]
87+
(let [p (fs/path dir agent-file-name)]
88+
(when (fs/readable? p)
89+
(str (fs/canonicalize p)))))
90+
{:keys [paths seen]}
91+
(reduce
92+
(fn [{:keys [paths seen]} {:keys [uri]}]
93+
(let [ws-path (safe-canonicalize (shared/uri->filename uri))
94+
candidate-dirs (cond-> []
95+
include-parents? (into (ancestor-paths ws-path))
96+
true (conj ws-path))
97+
new-paths (->> candidate-dirs
98+
(keep readable-agent-file-in)
99+
(remove seen))]
100+
{:paths (into paths new-paths)
101+
:seen (into seen new-paths)}))
102+
{:paths [] :seen #{}}
103+
(:workspace-folders db))
104+
global-agent-file (readable-agent-file-in (shared/global-config-dir))
105+
all-paths (cond-> paths
106+
(and global-agent-file (not (contains? seen global-agent-file)))
107+
(conj global-agent-file))]
108+
(mapcat parse-agents-file all-paths)))
76109

77110
(defn ^:private file->refined-context [path lines-range]
78111
(if (fs/readable? path)

src/eca/features/tools/mcp.clj

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,16 @@
296296
(defn ^:private try-refresh-token!
297297
"Attempt to refresh an MCP server's OAuth token.
298298
Returns true if refresh succeeded, false otherwise."
299-
[name db* url metrics {:keys [clientId clientSecret oauthPort]}]
299+
[name db* url metrics {:keys [clientId clientSecret oauthPort clientName]}]
300300
(let [mcp-auth (get-in @db* [:mcp-auth name])
301301
{:keys [refresh-token]} mcp-auth]
302302
(when refresh-token
303303
(logger/info logger-tag (format "Attempting to refresh token for MCP server '%s'" name))
304304
(when-let [oauth-info (oauth/oauth-info (replace-env-vars url)
305305
(some-> clientId replace-env-vars)
306306
(some-> clientSecret replace-env-vars)
307-
oauthPort)]
307+
oauthPort
308+
(some-> clientName replace-env-vars))]
308309
(when-let [new-tokens (oauth/refresh-token!
309310
(:token-endpoint oauth-info)
310311
(:client-id oauth-info)
@@ -367,7 +368,8 @@
367368
(oauth/oauth-info (replace-env-vars url)
368369
(some-> (:clientId server-config) replace-env-vars)
369370
(some-> (:clientSecret server-config) replace-env-vars)
370-
(:oauthPort server-config)))]
371+
(:oauthPort server-config)
372+
(some-> (:clientName server-config) replace-env-vars)))]
371373
(if oauth-info
372374
(initialize-mcp-oauth oauth-info
373375
name
@@ -387,6 +389,9 @@
387389
{:on-tools-change on-tools-change
388390
:pending-tools-refresh* pending-tools-refresh*})
389391
init-result (pmc/get-initialize-result client)
392+
_ (when-not init-result
393+
(throw (ex-info "MCP initialize returned no result"
394+
{:server name})))
390395
version (get-in init-result [:serverInfo :version])]
391396
(swap! db* assoc-in [:mcp-clients name] (cond-> {:client client
392397
:status :starting

0 commit comments

Comments
 (0)