Skip to content

Commit f26c5fe

Browse files
itkoneneca-agent
andcommitted
Fix MCP routing and OpenAI optional tool params
Route MCP tool calls through the server encoded by the selected full tool name so duplicate short names across servers do not collide. Also emit OpenAI Responses function tools with strict disabled to preserve optional MCP parameters. 🤖 Generated with [eca](https://eca.dev) Co-Authored-By: eca-agent <git@eca.dev>
1 parent d647690 commit f26c5fe

7 files changed

Lines changed: 54 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Bugfix: OpenAI Responses tool calls now opt out of strict schema normalization so optional tool parameters remain optional.
6+
- Bugfix: MCP tool calls now route to the selected server when multiple servers expose the same tool name.
7+
58
## 0.133.3
69

710
- Add unit and integration tests covering parent↔subagent end-to-end communication so regressions like the v0.133.1 spawn-agent breakage are caught automatically.

src/eca/features/tools.clj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,10 @@
256256
:call-state-fn call-state-fn
257257
:state-transition-fn state-transition-fn
258258
:trust trust})
259-
(f.mcp/call-tool! tool-name arguments {:db db
260-
:db* db*
261-
:config config
262-
:metrics metrics})))
259+
(f.mcp/call-tool! server-name tool-name arguments {:db db
260+
:db* db*
261+
:config config
262+
:metrics metrics})))
263263
(tools.util/maybe-truncate-output config tool-call-id))]
264264
(logger/debug logger-tag "Tool call result: " result)
265265
(metrics/count-up! "tool-called" {:name resolved-full-name :error (:error result)} metrics)

src/eca/features/tools/mcp.clj

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -855,13 +855,11 @@
855855
(do-call-tool new-client name arguments nil)
856856
(tool-call-error (format "Failed to re-initialize MCP server '%s'" server-name))))
857857

858-
(defn call-tool! [name arguments {:keys [db db* config metrics]}]
859-
(if-let [[server-name mcp-client needs-reinit?*]
860-
(->> (:mcp-clients db)
861-
(keep (fn [[sn {:keys [client tools needs-reinit?*]}]]
862-
(when (some #(= name (:name %)) tools)
863-
[sn client needs-reinit?*])))
864-
first)]
858+
(defn call-tool! [server-name name arguments {:keys [db db* config metrics]}]
859+
(if-let [[mcp-client needs-reinit?*]
860+
(when-let [{:keys [client tools needs-reinit?*]} (get-in db [:mcp-clients server-name])]
861+
(when (some #(= name (:name %)) tools)
862+
[client needs-reinit?*]))]
865863
(if (and needs-reinit?* @needs-reinit?* db* config metrics)
866864
;; Already flagged — reinit before attempting the call
867865
(reinit-and-call-tool! server-name mcp-client db* config metrics name arguments)
@@ -876,7 +874,7 @@
876874
(reinit-and-call-tool! server-name mcp-client db* config metrics name arguments)
877875

878876
:else result)))
879-
(tool-call-error (format "Tool '%s' not found in any connected MCP server" name))))
877+
(tool-call-error (format "Tool '%s' not found in MCP server '%s'" name server-name))))
880878

881879
(defn all-prompts [db]
882880
(into []

src/eca/features/tools/mcp/clojure_mcp.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
(when (not= "0.1.0" (:version server))
1010
(when ask-approval?
1111
(let [path (get args "file_path")
12-
{:keys [error contents]} (f.mcp/call-tool! name (assoc args "dry_run" "new-source") {:db db})]
12+
{:keys [error contents]} (f.mcp/call-tool! (:name server) name (assoc args "dry_run" "new-source") {:db db})]
1313
(when-not error
1414
(when-let [new-source (some->> contents (filter #(= :text (:type %))) first :text)]
1515
(let [{:keys [added removed diff]} (diff/diff (if new-file?

src/eca/llm_providers/openai.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@
183183
{:type "function"
184184
:name (:full-name tool)
185185
:description (:description tool)
186-
:parameters (:parameters tool)})
186+
:parameters (:parameters tool)
187+
:strict false})
187188
tools)
188189
web-search (conj {:type "web_search"})
189190
image-generation (conj {:type "image_generation" :output_format "png"})))

test/eca/features/tools/mcp_test.clj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@
4646
(mcp/all-tools {:mcp-clients {"server1" {:tools tools1}
4747
"server2" {:tools tools2}}}))))))
4848

49+
(deftest call-tool-routes-to-server-test
50+
(testing "uses the requested MCP server when tool names collide"
51+
(let [db {:mcp-clients (array-map
52+
"server-a" {:client :client-a
53+
:tools [{:name "shared_tool"}]}
54+
"server-b" {:client :client-b
55+
:tools [{:name "shared_tool"}]})}
56+
calls* (atom [])]
57+
(with-redefs [mcp/do-call-tool (fn [client name arguments needs-reinit?*]
58+
(swap! calls* conj {:client client
59+
:name name
60+
:arguments arguments
61+
:needs-reinit?* needs-reinit?*})
62+
{:error false
63+
:contents [{:type :text :text (str client)}]})]
64+
(is (= {:error false
65+
:contents [{:type :text :text ":client-b"}]}
66+
(mcp/call-tool! "server-b" "shared_tool" {"query" "value"} {:db db})))
67+
(is (= [{:client :client-b
68+
:name "shared_tool"
69+
:arguments {"query" "value"}
70+
:needs-reinit?* nil}]
71+
@calls*))))))
72+
4973
(deftest all-prompts-test
5074
(testing "empty db"
5175
(is (= []

test/eca/llm_providers/openai_test.clj

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,20 @@
409409
{:type "image_generation" :output_format "png"}]
410410
(#'llm-providers.openai/->tools
411411
[{:full-name "eca__foo" :description "d" :parameters {}}]
412-
true true)))))
412+
true true))))
413+
(testing "function tools explicitly opt out of Responses strict mode"
414+
(is (= [{:type "function"
415+
:name "mcp__search_records"
416+
:description "Search records"
417+
:parameters {:type "object"
418+
:properties {"limit" {:type "number"}}}
419+
:strict false}]
420+
(#'llm-providers.openai/->tools
421+
[{:full-name "mcp__search_records"
422+
:description "Search records"
423+
:parameters {:type "object"
424+
:properties {"limit" {:type "number"}}}}]
425+
false false)))))
413426

414427
(deftest create-response-oauth-preserves-built-in-tools-test
415428
(testing "OAuth requests keep web_search and image_generation when capabilities are enabled"

0 commit comments

Comments
 (0)