Skip to content

Commit 5f62ad4

Browse files
authored
Merge pull request #458 from itkonen/fix-mcp-routing-openai-strict
Fix MCP routing and OpenAI optional tool params
2 parents ca4689d + 45cef19 commit 5f62ad4

7 files changed

Lines changed: 53 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
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.
57
- 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.
68

79
## 0.134.1

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
@@ -860,13 +860,11 @@
860860
(do-call-tool new-client name arguments nil)
861861
(tool-call-error (format "Failed to re-initialize MCP server '%s'" server-name))))
862862

863-
(defn call-tool! [name arguments {:keys [db db* config metrics]}]
864-
(if-let [[server-name mcp-client needs-reinit?*]
865-
(->> (:mcp-clients db)
866-
(keep (fn [[sn {:keys [client tools needs-reinit?*]}]]
867-
(when (some #(= name (:name %)) tools)
868-
[sn client needs-reinit?*])))
869-
first)]
863+
(defn call-tool! [server-name name arguments {:keys [db db* config metrics]}]
864+
(if-let [[mcp-client needs-reinit?*]
865+
(when-let [{:keys [client tools needs-reinit?*]} (get-in db [:mcp-clients server-name])]
866+
(when (some #(= name (:name %)) tools)
867+
[client needs-reinit?*]))]
870868
(if (and needs-reinit?* @needs-reinit?* db* config metrics)
871869
;; Already flagged — reinit before attempting the call
872870
(reinit-and-call-tool! server-name mcp-client db* config metrics name arguments)
@@ -881,7 +879,7 @@
881879
(reinit-and-call-tool! server-name mcp-client db* config metrics name arguments)
882880

883881
:else result)))
884-
(tool-call-error (format "Tool '%s' not found in any connected MCP server" name))))
882+
(tool-call-error (format "Tool '%s' not found in MCP server '%s'" name server-name))))
885883

886884
(defn all-prompts [db]
887885
(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)