From c7baafcb0eae43a89ef91cee66f2ed942728e2c6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 02:09:36 +0000 Subject: [PATCH 1/6] fix(binding-mcp-http): support bare ${result} in tool.summary resultReferences() only recognized the ${result.} form, requiring a nested object key; resolveCapturedResult() mirrored that restriction. A response body that is itself a bare scalar had no way to feed tool.summary at all. Recognize a bare ${result} reference (no path) as the response's own root value: McpHttpResults arms the capture for the empty-path reference up front (there is no KEY_NAME to trigger it), skipping the pipeline's own START_DOCUMENT/END_DOCUMENT framing events so the real root value is what gets captured. Adds get_status, a tool with no declared output schema whose upstream response is a bare JSON string, to prove the new reference end-to-end. Fixes https://github.com/aklivity/zilla/issues/2535 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AvsVDMfv1iedqo667q74JT --- .../internal/stream/McpHttpProxyFactory.java | 40 +++++++---- .../internal/transform/McpHttpResults.java | 19 +++++- .../http/internal/stream/McpHttpClientIT.java | 12 ++++ .../http/internal/stream/McpHttpProxyIT.java | 12 ++++ .../specs/binding/mcp/http/config/client.yaml | 27 ++++++++ .../specs/binding/mcp/http/config/proxy.yaml | 28 ++++++++ .../http/streams/http/get.status/client.rpt | 39 +++++++++++ .../http/streams/http/get.status/server.rpt | 46 +++++++++++++ .../http/streams/mcp/get.status/client.rpt | 65 ++++++++++++++++++ .../http/streams/mcp/get.status/server.rpt | 66 +++++++++++++++++++ .../mcp/http/streams/HttpClientIT.java | 9 +++ .../binding/mcp/http/streams/McpServerIT.java | 9 +++ 12 files changed, 358 insertions(+), 14 deletions(-) create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/client.rpt create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/server.rpt create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/client.rpt create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/server.rpt diff --git a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java index ea25f6b2bff..f4f2fee91ff 100644 --- a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java +++ b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java @@ -1436,15 +1436,17 @@ private JsonPipeline.Status errorRelayStep( return status; } - // Resolves a result. reference from the values McpHttpResults captured while structuredContent - // streamed past, replacing a re-scan of a fully buffered response copy. + // Resolves a result. reference, or a bare result reference (the response's own root value), + // from the values McpHttpResults captured while structuredContent streamed past, replacing a + // re-scan of a fully buffered response copy. private String resolveCapturedResult( String expression) { String value = ""; - if (expression.startsWith("result.")) + if (expression.equals("result") || expression.startsWith("result.")) { - final String captured = capturedResults.get(expression.substring(7)); + final String path = expression.equals("result") ? "" : expression.substring(7); + final String captured = capturedResults.get(path); value = captured != null ? captured : ""; } return value; @@ -2810,7 +2812,8 @@ private static List argReferences( // Extracts the result. references from a tool.summary template (e.g. "result.number" from // "Created pull request #${result.number}"), the set McpHttpResults is asked to capture as the response - // streams past. + // streams past. A bare ${result} (no path) — the response body's own root value — is recorded as the + // empty-string path, McpHttpResults' sentinel for a root capture. private static List resultReferences( String template) { @@ -2818,17 +2821,30 @@ private static List resultReferences( if (template != null) { int index = 0; - int start = template.indexOf("${result.", index); + int start = template.indexOf("${result", index); while (start >= 0) { - final int end = template.indexOf('}', start); - if (end < 0) + final int afterKeyword = start + 8; + if (afterKeyword < template.length() && template.charAt(afterKeyword) == '.') { - break; + final int end = template.indexOf('}', afterKeyword); + if (end < 0) + { + break; + } + result.add(template.substring(afterKeyword + 1, end)); + index = end + 1; } - result.add(template.substring(start + 9, end)); - index = end + 1; - start = template.indexOf("${result.", index); + else if (afterKeyword < template.length() && template.charAt(afterKeyword) == '}') + { + result.add(""); + index = afterKeyword + 1; + } + else + { + index = afterKeyword; + } + start = template.indexOf("${result", index); } } return result; diff --git a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java index 41571c94352..b755cbc0bb9 100644 --- a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java +++ b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java @@ -42,6 +42,15 @@ * captured "in flight" at a time (JSON parsing is strictly sequential), a single reused accumulator is * sufficient. *

+ * The empty path (zero segments) denotes a bare {@code result} reference — the response body's own root + * value, with no key to match a {@code KEY_NAME} against — so it is armed as {@code awaiting} up front by + * {@link #reset()} instead of via {@link #onKeyName}. {@link #transform} excludes the pipeline's own + * {@code START_DOCUMENT}/{@code END_DOCUMENT} framing events from reaching {@link #capture} so that framing, + * not the real root value, is what the root reference would otherwise capture first; the first real event + * after that is the document's own root, whatever it turns out to be. A root that is an object or array + * gives up immediately (the same structural-value fallback {@link #capture} already applies to a keyed + * path), leaving that reference unresolved. + *

* This is a mediating, structure-inspecting transform sitting in front of a byte-preferring terminal sink * (see {@code common-json}'s verbatim-validate design notes), so it cannot forward a downstream * {@link JsonController#segmentable()} request upstream unchanged the way a purely forwarding stage would: @@ -55,6 +64,8 @@ */ public final class McpHttpResults implements JsonTransform { + private static final String[] EMPTY_SEGMENTS = new String[0]; + private final Map captured; private final String[] paths; private final String[][] segments; @@ -98,7 +109,7 @@ public McpHttpResults( this.segments = new String[this.paths.length][]; for (int i = 0; i < this.paths.length; i++) { - this.segments[i] = this.paths[i].split("\\."); + this.segments[i] = this.paths[i].isEmpty() ? EMPTY_SEGMENTS : this.paths[i].split("\\."); } this.matched = new int[this.paths.length]; } @@ -112,6 +123,10 @@ public void reset() for (int i = 0; i < matched.length; i++) { matched[i] = 0; + if (segments[i].length == 0) + { + awaiting = i; + } } } @@ -129,7 +144,7 @@ public Status transform( JsonSink sink) { upstream = control; - if (awaiting != -1) + if (awaiting != -1 && event != JsonEvent.START_DOCUMENT && event != JsonEvent.END_DOCUMENT) { capture(awaiting, event, source); } diff --git a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java index 43c0dd8f077..2a4a87d3c43 100644 --- a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java +++ b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java @@ -399,6 +399,18 @@ public void shouldCallToolCountItems() throws Exception k3po.finish(); } + // get_status has no declared output schema at all, so the raw upstream body streams unprojected; + // tool.summary's bare ${result} reference (no path) binds to that response's own root scalar value + @Test + @Configuration("client.yaml") + @Specification({ + "${mcp}/get.status/client", + "${http}/get.status/server"}) + public void shouldCallToolGetStatus() throws Exception + { + k3po.finish(); + } + // a 12000-byte top-level argument value referenced by the route's :path template: proves // McpHttpArguments captures a value spanning multiple decode windows correctly (see the // mediating-transform rule / multi-window accumulation fix), not just short path arguments that diff --git a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java index eba8dd415cd..07eee45ae8b 100644 --- a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java +++ b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java @@ -397,6 +397,18 @@ public void shouldCallToolCountItems() throws Exception k3po.finish(); } + // get_status has no declared output schema at all, so the raw upstream body streams unprojected; + // tool.summary's bare ${result} reference (no path) binds to that response's own root scalar value + @Test + @Configuration("proxy.yaml") + @Specification({ + "${mcp}/get.status/client", + "${http}/get.status/server"}) + public void shouldCallToolGetStatus() throws Exception + { + k3po.finish(); + } + // a 12000-byte top-level argument value referenced by the route's :path template: proves // McpHttpArguments captures a value spanning multiple decode windows correctly (see the // mediating-transform rule / multi-window accumulation fix), not just short path arguments that diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml index 0e4b356c146..e745a557047 100644 --- a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml @@ -205,6 +205,13 @@ catalogs: { "type": "integer" } + get_status_params: + version: latest + schema: | + { + "type": "object", + "properties": {} + } echo_id_params: version: latest schema: | @@ -403,6 +410,18 @@ bindings: http_github0: - subject: count_items_result version: latest + get_status: + description: > + Get service status. A bare-scalar response with no declared output schema, covering + tool.summary's bare ${result} reference (the response's own root value, with no path). + summary: "Status: ${result}" + schemas: + input: + model: json + catalog: + http_github0: + - subject: get_status_params + version: latest echo_id: description: > Echo the given identifier. @@ -573,6 +592,14 @@ bindings: ":scheme": https ":authority": api.github.com:443 ":path": /count + - when: + - tool: get_status + with: + headers: + ":method": GET + ":scheme": https + ":authority": api.github.com:443 + ":path": /status - when: - tool: echo_id with: diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml index bc4403404c7..4144171c946 100644 --- a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml @@ -205,6 +205,13 @@ catalogs: { "type": "integer" } + get_status_params: + version: latest + schema: | + { + "type": "object", + "properties": {} + } echo_id_params: version: latest schema: | @@ -403,6 +410,18 @@ bindings: http_github0: - subject: count_items_result version: latest + get_status: + description: > + Get service status. A bare-scalar response with no declared output schema, covering + tool.summary's bare ${result} reference (the response's own root value, with no path). + summary: "Status: ${result}" + schemas: + input: + model: json + catalog: + http_github0: + - subject: get_status_params + version: latest echo_id: description: > Echo the given identifier. @@ -582,6 +601,15 @@ bindings: ":scheme": https ":authority": api.github.com:443 ":path": /count + - when: + - tool: get_status + exit: http0 + with: + headers: + ":method": GET + ":scheme": https + ":authority": api.github.com:443 + ":path": /status - when: - tool: echo_id exit: http0 diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/client.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/client.rpt new file mode 100644 index 00000000000..16c7c0d5af9 --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/client.rpt @@ -0,0 +1,39 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +connect "zilla://streams/http0" + option zilla:window 8192 + option zilla:transmission "half-duplex" + +write zilla:begin.ext ${http:beginEx() + .typeId(zilla:id("http")) + .header(":method", "GET") + .header(":scheme", "https") + .header(":authority", "api.github.com:443") + .header(":path", "/status") + .build()} + +connected + +read zilla:begin.ext ${http:matchBeginEx() + .typeId(zilla:id("http")) + .header(":status", "200") + .header("content-type", "application/json") + .build()} + +read '"healthy"' +read closed + +write close diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/server.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/server.rpt new file mode 100644 index 00000000000..0a4bfbddc4d --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.status/server.rpt @@ -0,0 +1,46 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +property serverAddress "zilla://streams/http0" + +accept ${serverAddress} + option zilla:window 8192 + option zilla:transmission "half-duplex" + +accepted + +read zilla:begin.ext ${http:matchBeginEx() + .typeId(zilla:id("http")) + .header(":method", "GET") + .header(":scheme", "https") + .header(":authority", "api.github.com:443") + .header(":path", "/status") + .build()} + +connected + +write zilla:begin.ext ${http:beginEx() + .typeId(zilla:id("http")) + .header(":status", "200") + .header("content-type", "application/json") + .build()} +write flush + +write '"healthy"' +write flush + +write close + +read closed diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/client.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/client.rpt new file mode 100644 index 00000000000..ab8221be35b --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/client.rpt @@ -0,0 +1,65 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +connect "zilla://streams/app0" + option zilla:window 8192 + option zilla:transmission "half-duplex" + +write zilla:begin.ext ${mcp:beginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .build() + .build()} + +connected + +read zilla:begin.ext ${mcp:matchBeginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .build() + .build()} + +read notify LIFECYCLE_INITIALIZED + +connect await LIFECYCLE_INITIALIZED + "zilla://streams/app0" + option zilla:window 8192 + option zilla:transmission "half-duplex" + +write zilla:begin.ext ${mcp:beginEx() + .typeId(zilla:id("mcp")) + .toolsCall() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .name("get_status") + .contentLength(36) + .build() + .build()} + +connected + +write '{"name":"get_status","arguments":{}}' + +# tool has no output schema, so the raw upstream body (a bare JSON string, not an object) streams +# through unprojected; tool.summary's bare ${result} reference (no path) binds to that whole root +# scalar value, proving the reference works with no wrapping key to match against +read '{' + '"structuredContent":"healthy",' + '"content":[{"type":"text","text":"Status: healthy"}],' + '"isError":false' + '}' +read closed + +write close diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/server.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/server.rpt new file mode 100644 index 00000000000..2ec2721022f --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.status/server.rpt @@ -0,0 +1,66 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +property serverAddress "zilla://streams/app0" + +accept ${serverAddress} + option zilla:window 8192 + option zilla:transmission "half-duplex" + +accepted + +read zilla:begin.ext ${mcp:matchBeginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .build() + .build()} + +connected + +write zilla:begin.ext ${mcp:beginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .build() + .build()} +write flush + +accepted + +read zilla:begin.ext ${mcp:matchBeginEx() + .typeId(zilla:id("mcp")) + .toolsCall() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .name("get_status") + .contentLength(36) + .build() + .build()} + +connected + +read '{"name":"get_status","arguments":{}}' + +write flush + +write '{' + '"structuredContent":"healthy",' + '"content":[{"type":"text","text":"Status: healthy"}],' + '"isError":false' + '}' +write flush + +write close + +read closed diff --git a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java index 0af3d461b8d..f0bf0123f93 100644 --- a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java +++ b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java @@ -225,6 +225,15 @@ public void shouldProxyCountItemsToHttp() throws Exception k3po.finish(); } + @Test + @Specification({ + "${http}/get.status/client", + "${http}/get.status/server"}) + public void shouldProxyGetStatusToHttp() throws Exception + { + k3po.finish(); + } + @Test @Specification({ "${http}/echo.id.large/client", diff --git a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java index 22c12da3029..72bc101bd4b 100644 --- a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java +++ b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java @@ -315,6 +315,15 @@ public void shouldCallToolCountItems() throws Exception k3po.finish(); } + @Test + @Specification({ + "${mcp}/get.status/client", + "${mcp}/get.status/server"}) + public void shouldCallToolGetStatus() throws Exception + { + k3po.finish(); + } + @Test @Specification({ "${mcp}/echo.id.large/client", From 4ec19d44b8d54fea13f8647e8c9c396f064bb47c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 02:14:09 +0000 Subject: [PATCH 2/6] fix(binding-mcp-http): satisfy EqualsAvoidNull checkstyle rule Literal-vs-variable equals() checks flagged by checkstyle in resolveCapturedResult(), missed in the prior commit. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AvsVDMfv1iedqo667q74JT --- .../binding/mcp/http/internal/stream/McpHttpProxyFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java index f4f2fee91ff..c313225fac2 100644 --- a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java +++ b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java @@ -1443,9 +1443,9 @@ private String resolveCapturedResult( String expression) { String value = ""; - if (expression.equals("result") || expression.startsWith("result.")) + if ("result".equals(expression) || expression.startsWith("result.")) { - final String path = expression.equals("result") ? "" : expression.substring(7); + final String path = "result".equals(expression) ? "" : expression.substring(7); final String captured = capturedResults.get(path); value = captured != null ? captured : ""; } From a6a088400181c6414d8efe59e3e571de8828df4d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 02:57:28 +0000 Subject: [PATCH 3/6] feat(binding-mcp-http): serialize object/array values in tool.summary McpHttpResults only captured scalar values at a ${result.} reference, giving up on an object or array with no way to fall back to anything but an empty string. Add container capture: once a watched path's value turns out to be an object or array, re-serialize the whole subtree into the captured text as compact JSON instead of giving up, covering nested strings (with full JSON escaping), numbers, booleans, null, and arbitrarily nested containers. A path whose in-flight container capture is still open now also blocks onKeyName() from arming a second, different path mid-subtree, which the prior scalar-only design never needed to guard against. Demonstrated end-to-end with a new get_dashboard tool (a nested-object container capture alongside scalar/null/boolean leaf references in the same template) and by pointing list_tags' existing array-rooted tool.summary at the new bare ${result} capture. This is what makes ${result.result} usable against mcp-openapi's wrap-as-{"result":} envelope for a non-object OpenAPI response, so seven generic, non-templated tool.summary strings across binding-mcp-schema-registry and binding-mcp-kafka-connect (their responses are root-level arrays or a bare integer) now interpolate the real response instead. Also fixes a k3po script-escaping bug this surfaced: a k3po single- or double-quoted string literal treats backslash as its own escape character, so embedding a literal backslash in expected wire bytes (as several pre-existing and new .rpt scripts needed to, once summaries started containing JSON-escaped content) requires doubling it in the script source -- a bare backslash-quote sequence is silently consumed by k3po's own lexer before the engine ever sees the script. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AvsVDMfv1iedqo667q74JT --- .../internal/transform/McpHttpResults.java | 245 ++++++++++++++++-- .../http/internal/stream/McpHttpClientIT.java | 16 +- .../http/internal/stream/McpHttpProxyIT.java | 16 +- .../schema/kafka-connect.openapi.json | 6 +- .../karapace-schema-registry.openapi.json | 2 +- .../specs/binding/mcp/http/config/client.yaml | 30 ++- .../specs/binding/mcp/http/config/proxy.yaml | 31 ++- .../streams/http/get.dashboard/client.rpt | 40 +++ .../streams/http/get.dashboard/server.rpt | 47 ++++ .../http/streams/mcp/get.dashboard/client.rpt | 64 +++++ .../http/streams/mcp/get.dashboard/server.rpt | 62 +++++ .../mcp/http/streams/mcp/list.tags/client.rpt | 6 +- .../mcp/http/streams/mcp/list.tags/server.rpt | 2 +- .../mcp/http/streams/HttpClientIT.java | 9 + .../binding/mcp/http/streams/McpServerIT.java | 9 + .../mcp/list.connector.plugins/client.rpt | 2 +- .../mcp/list.connector.plugins/server.rpt | 2 +- .../mcp/list.connector.tasks/client.rpt | 2 +- .../mcp/list.connector.tasks/server.rpt | 2 +- .../streams/mcp/list.connectors/client.rpt | 2 +- .../streams/mcp/list.connectors/server.rpt | 2 +- .../mcp/delete.schema.version/client.rpt | 2 +- .../mcp/delete.schema.version/server.rpt | 2 +- .../streams/mcp/delete.subject/client.rpt | 2 +- .../streams/mcp/delete.subject/server.rpt | 2 +- .../streams/mcp/describe.subject/client.rpt | 2 +- .../streams/mcp/describe.subject/server.rpt | 2 +- .../streams/mcp/list.subjects/client.rpt | 2 +- .../streams/mcp/list.subjects/server.rpt | 2 +- 29 files changed, 561 insertions(+), 52 deletions(-) create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/client.rpt create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/server.rpt create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/client.rpt create mode 100644 specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/server.rpt diff --git a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java index b755cbc0bb9..3cd5deddaa6 100644 --- a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java +++ b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/transform/McpHttpResults.java @@ -25,31 +25,39 @@ import io.aklivity.zilla.runtime.common.json.JsonTransform; /** - * Watches a {@code tools/call} response body as it streams past and captures the scalar value at each of a - * fixed set of dotted {@code result.} references (e.g. {@code number}, {@code data.id}) — the paths a - * configured {@code tool.summary} template interpolates — into {@code captured}, while forwarding every event - * downstream unchanged ({@link #identity()} returns {@code true}). This lets a summary be resolved once the - * response has fully streamed past without re-scanning a buffered copy, the response-side counterpart to + * Watches a {@code tools/call} response body as it streams past and captures the value at each of a fixed set + * of dotted {@code result.} references (e.g. {@code number}, {@code data.id}) — the paths a configured + * {@code tool.summary} template interpolates — into {@code captured}, while forwarding every event downstream + * unchanged ({@link #identity()} returns {@code true}). This lets a summary be resolved once the response has + * fully streamed past without re-scanning a buffered copy, the response-side counterpart to * {@link McpHttpArguments}'s request-side capture. *

* Each configured path tracks its own match progress independently against the shared document depth, mirroring * the depth/matched-segment algorithm a one-shot buffered lookup would use (match segment {@code n} of a path * only at a {@code KEY_NAME} seen at depth {@code n + 1}), extended to watch several paths — of possibly - * different lengths and depths — over one incremental pass instead of one re-scan per path. Only scalar events - * ({@code VALUE_STRING}, {@code VALUE_NUMBER}, {@code VALUE_TRUE}, {@code VALUE_FALSE}) are captured. A value - * spanning more than one input window accumulates across every fragment into {@code text} and only commits - * once {@link JsonSource#deferredBytes()} reports the value complete; since only one value can ever be - * captured "in flight" at a time (JSON parsing is strictly sequential), a single reused accumulator is - * sufficient. + * different lengths and depths — over one incremental pass instead of one re-scan per path. + *

+ * A path whose value turns out to be a scalar ({@code VALUE_STRING}, {@code VALUE_NUMBER}, {@code VALUE_TRUE}, + * {@code VALUE_FALSE}, {@code VALUE_NULL}) captures that scalar's own text verbatim (a string capturing its + * content unquoted, a number its digits, {@code true}/{@code false}/{@code null} their literal spelling) — + * exactly what a template author who wrote {@code Created pull request #${result.number}} expects to see + * substituted in place of {@code ${result.number}}. A path whose value turns out to be an object or array + * instead re-serializes that whole subtree into {@code text} as compact JSON (see {@link #captureContainer}), + * so a template can still say something meaningful about a response shaped as a list or nested record even + * though there is no single scalar to point at. A value spanning more than one input window accumulates across + * every fragment into {@code text} and only commits once {@link JsonSource#deferredBytes()} reports the value + * complete (for a container capture, once the container's own matching close event is reached); since only one + * value can ever be captured "in flight" at a time (JSON parsing is strictly sequential), a single reused + * accumulator is sufficient. While one path's capture is in flight ({@code awaiting != -1}), {@link #onKeyName} + * is skipped entirely — a nested key inside the very subtree being serialized must never be allowed to arm a + * second, different path and corrupt the first one's still-open accumulation. *

* The empty path (zero segments) denotes a bare {@code result} reference — the response body's own root * value, with no key to match a {@code KEY_NAME} against — so it is armed as {@code awaiting} up front by * {@link #reset()} instead of via {@link #onKeyName}. {@link #transform} excludes the pipeline's own * {@code START_DOCUMENT}/{@code END_DOCUMENT} framing events from reaching {@link #capture} so that framing, * not the real root value, is what the root reference would otherwise capture first; the first real event - * after that is the document's own root, whatever it turns out to be. A root that is an object or array - * gives up immediately (the same structural-value fallback {@link #capture} already applies to a keyed - * path), leaving that reference unresolved. + * after that is the document's own root, whatever shape it turns out to be. *

* This is a mediating, structure-inspecting transform sitting in front of a byte-preferring terminal sink * (see {@code common-json}'s verbatim-validate design notes), so it cannot forward a downstream @@ -65,6 +73,7 @@ public final class McpHttpResults implements JsonTransform { private static final String[] EMPTY_SEGMENTS = new String[0]; + private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); private final Map captured; private final String[] paths; @@ -99,6 +108,8 @@ public void consumed( private JsonController upstream; private int depth; private int awaiting = -1; + private int captureDepth; + private boolean nestedValueOpen; public McpHttpResults( Map captured, @@ -119,6 +130,8 @@ public void reset() { depth = 0; awaiting = -1; + captureDepth = 0; + nestedValueOpen = false; text.setLength(0); for (int i = 0; i < matched.length; i++) { @@ -160,7 +173,12 @@ public Status transform( depth--; break; case KEY_NAME: - onKeyName(source); + // a nested key inside a subtree already being serialized for some other in-flight capture + // must never be allowed to arm a second, different path -- see the class Javadoc + if (awaiting == -1) + { + onKeyName(source); + } break; default: break; @@ -210,40 +228,213 @@ private void onKeyName( // Appends this fragment to the in-flight value's accumulator, committing to `captured` (and clearing // `awaiting`, so the next KEY_NAME can arm a fresh capture) only once deferredBytes() reports no more - // fragments follow; a structural value (an object/array where a scalar was expected) gives up - // immediately, matching the existing unresolved-path fallback to the empty string. A VERBATIM event - // rides alongside the structured event stream for the same value rather than substituting for it (see - // the class Javadoc), so it is ignored here rather than treated as an unexpected structural event. + // fragments follow. A VERBATIM event rides alongside the structured event stream for the same value + // rather than substituting for it (see the class Javadoc), so it is ignored here rather than treated as + // an unexpected event. Once already inside a container capture (captureDepth > 0), every event -- keys, + // nested values, nested containers -- routes to captureContainer() instead; a START_OBJECT/START_ARRAY + // seen here for the first time is what puts it there. private void capture( int index, JsonEvent event, JsonSource source) + { + if (captureDepth > 0 || event == JsonEvent.START_OBJECT || event == JsonEvent.START_ARRAY) + { + captureContainer(index, event, source); + } + else + { + switch (event) + { + case VALUE_STRING: + case VALUE_NUMBER: + text.append(source.getStringView()); + if (!source.deferredBytes()) + { + captured.put(paths[index], text.toString()); + text.setLength(0); + awaiting = -1; + } + break; + case VALUE_TRUE: + captured.put(paths[index], "true"); + awaiting = -1; + break; + case VALUE_FALSE: + captured.put(paths[index], "false"); + awaiting = -1; + break; + case VALUE_NULL: + captured.put(paths[index], "null"); + awaiting = -1; + break; + case VERBATIM: + break; + default: + awaiting = -1; + break; + } + } + } + + // Re-serializes an object/array value into `text` as compact JSON while it streams past, one event at a + // time, tracking captureDepth (the nesting depth *within this captured subtree*, independent of the + // document-wide `depth` field) to know when the value's own matching close event is reached. A comma is + // owed before any new key or value except the first child of a container or the value right after a key + // -- appendSeparator() decides this from the last character already written rather than a separate + // "first child" flag per nesting level, since '{', '[' and ':' are exactly the characters nothing else + // in a compact JSON document ever ends a token with. A string or number value spanning more than one + // input window is tracked via nestedValueOpen the same way the top-level scalar case tracks it via + // deferredBytes() directly -- the leading separator/quote is written only for the first fragment, the + // closing quote only for the last. + private void captureContainer( + int index, + JsonEvent event, + JsonSource source) { switch (event) { + case START_OBJECT: + appendSeparator(); + text.append('{'); + captureDepth++; + break; + case START_ARRAY: + appendSeparator(); + text.append('['); + captureDepth++; + break; + case END_OBJECT: + text.append('}'); + closeContainer(index); + break; + case END_ARRAY: + text.append(']'); + closeContainer(index); + break; + case KEY_NAME: + appendSeparator(); + text.append('"'); + appendEscaped(source.getStringView()); + text.append('"').append(':'); + break; case VALUE_STRING: + if (!nestedValueOpen) + { + appendSeparator(); + text.append('"'); + nestedValueOpen = true; + } + appendEscaped(source.getStringView()); + if (!source.deferredBytes()) + { + text.append('"'); + nestedValueOpen = false; + } + break; case VALUE_NUMBER: + if (!nestedValueOpen) + { + appendSeparator(); + nestedValueOpen = true; + } text.append(source.getStringView()); if (!source.deferredBytes()) { - captured.put(paths[index], text.toString()); - text.setLength(0); - awaiting = -1; + nestedValueOpen = false; } break; case VALUE_TRUE: - captured.put(paths[index], "true"); - awaiting = -1; + appendSeparator(); + text.append("true"); break; case VALUE_FALSE: - captured.put(paths[index], "false"); - awaiting = -1; + appendSeparator(); + text.append("false"); + break; + case VALUE_NULL: + appendSeparator(); + text.append("null"); break; case VERBATIM: break; default: - awaiting = -1; break; } } + + // A comma is owed before a new key or value unless it would be the first child of the container just + // opened (last character '{' or '[') or the value immediately following a key (last character ':') -- + // none of which a completed value (a closing quote, digit, e/l, or nested '}'/']') ever ends with, so + // this single check replaces a separate "first child of this nesting level" flag per depth. + private void appendSeparator() + { + if (text.length() > 0) + { + final char last = text.charAt(text.length() - 1); + if (last != '{' && last != '[' && last != ':') + { + text.append(','); + } + } + } + + // captureDepth reaching zero means the close event just appended matched the container capture's own + // opening event, so the fully reconstructed subtree in `text` is ready to commit. + private void closeContainer( + int index) + { + captureDepth--; + if (captureDepth == 0) + { + captured.put(paths[index], text.toString()); + text.setLength(0); + awaiting = -1; + } + } + + private void appendEscaped( + CharSequence value) + { + for (int i = 0; i < value.length(); i++) + { + final char c = value.charAt(i); + switch (c) + { + case '"': + text.append("\\\""); + break; + case '\\': + text.append("\\\\"); + break; + case '\n': + text.append("\\n"); + break; + case '\r': + text.append("\\r"); + break; + case '\t': + text.append("\\t"); + break; + case '\b': + text.append("\\b"); + break; + case '\f': + text.append("\\f"); + break; + default: + if (c < 0x20) + { + text.append("\\u00") + .append(HEX_DIGITS[(c >> 4) & 0xf]) + .append(HEX_DIGITS[c & 0xf]); + } + else + { + text.append(c); + } + break; + } + } + } } diff --git a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java index 2a4a87d3c43..874f8c12851 100644 --- a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java +++ b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpClientIT.java @@ -376,7 +376,7 @@ public void shouldCallToolPing() throws Exception } // list_tags' output schema is array-rooted: structuredContent is the array itself, not an object - // wrapping it + // wrapping it; tool.summary's bare ${result} captures that same root array as compact JSON @Test @Configuration("client.yaml") @Specification({ @@ -411,6 +411,20 @@ public void shouldCallToolGetStatus() throws Exception k3po.finish(); } + // get_dashboard's tool.summary combines a container reference (${result.data}, a nested object + // re-serialized as compact JSON, including a quote character that must round-trip through JSON + // escaping) with bare scalar/null/boolean leaf references (${result.active}, ${result.disabled}, + // ${result.flag}) in the same template + @Test + @Configuration("client.yaml") + @Specification({ + "${mcp}/get.dashboard/client", + "${http}/get.dashboard/server"}) + public void shouldCallToolGetDashboard() throws Exception + { + k3po.finish(); + } + // a 12000-byte top-level argument value referenced by the route's :path template: proves // McpHttpArguments captures a value spanning multiple decode windows correctly (see the // mediating-transform rule / multi-window accumulation fix), not just short path arguments that diff --git a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java index 07eee45ae8b..02b85d2e4b9 100644 --- a/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java +++ b/runtime/binding-mcp-http/src/test/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyIT.java @@ -374,7 +374,7 @@ public void shouldCallToolPing() throws Exception } // list_tags' output schema is array-rooted: structuredContent is the array itself, not an object - // wrapping it + // wrapping it; tool.summary's bare ${result} captures that same root array as compact JSON @Test @Configuration("proxy.yaml") @Specification({ @@ -409,6 +409,20 @@ public void shouldCallToolGetStatus() throws Exception k3po.finish(); } + // get_dashboard's tool.summary combines a container reference (${result.data}, a nested object + // re-serialized as compact JSON, including a quote character that must round-trip through JSON + // escaping) with bare scalar/null/boolean leaf references (${result.active}, ${result.disabled}, + // ${result.flag}) in the same template + @Test + @Configuration("proxy.yaml") + @Specification({ + "${mcp}/get.dashboard/client", + "${http}/get.dashboard/server"}) + public void shouldCallToolGetDashboard() throws Exception + { + k3po.finish(); + } + // a 12000-byte top-level argument value referenced by the route's :path template: proves // McpHttpArguments captures a value spanning multiple decode windows correctly (see the // mediating-transform rule / multi-window accumulation fix), not just short path arguments that diff --git a/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json b/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json index b102237a811..df91d752d7f 100644 --- a/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json +++ b/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json @@ -13,7 +13,7 @@ "/connectors": { "get": { "operationId": "list_connectors", - "summary": "Listed connectors", + "summary": "Connectors: ${result.result}", "responses": { "200": { "description": "ok" @@ -303,7 +303,7 @@ "/connectors/{connector}/tasks": { "get": { "operationId": "list_connector_tasks", - "summary": "Listed connector tasks", + "summary": "Tasks: ${result.result}", "parameters": [ { "name": "connector", @@ -452,7 +452,7 @@ "/connector-plugins": { "get": { "operationId": "list_connector_plugins", - "summary": "Listed connector plugins", + "summary": "Plugins: ${result.result}", "responses": { "200": { "description": "ok" diff --git a/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json b/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json index 88aac062730..0ac3afafc8b 100644 --- a/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json +++ b/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"Schema Registry API","version":"1.0"},"servers":[{"url":"http://localhost:8080"}],"paths":{"/subjects":{"get":{"operationId":"list_subjects","description":"List all registered subjects in the schema registry.","summary":"List subjects","responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions":{"get":{"operationId":"describe_subject","description":"List the schema version numbers registered for a subject.","summary":"List versions for a subject","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"operationId":"register_schema","description":"Register a new schema version under a subject.","summary":"Registered schema with id ${result.id}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions/{version}":{"get":{"operationId":"get_schema","description":"Retrieve a specific registered schema version for a subject.","summary":"Retrieved schema id ${result.id}, version ${result.version}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"delete":{"operationId":"delete_schema_version","description":"Delete a specific registered schema version for a subject.","summary":"Delete a specific schema version","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}":{"delete":{"operationId":"delete_subject","description":"Delete a subject and all its registered schema versions.","summary":"Delete a subject and all its versions","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/compatibility/subjects/{subject}/versions/{version}":{"post":{"operationId":"check_compatibility","description":"Check whether a given schema is compatible with the existing versions registered for a subject.","summary":"Compatibility check result: ${result.is_compatible}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/config/{subject}":{"get":{"operationId":"get_compatibility","description":"Get the compatibility level configured for a subject.","summary":"Compatibility level is ${result.compatibilityLevel}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"operationId":"set_compatibility","description":"Set the compatibility level for a subject.","summary":"Compatibility level set to ${result.compatibility}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"compatibility":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"Schema Registry API","version":"1.0"},"servers":[{"url":"http://localhost:8080"}],"paths":{"/subjects":{"get":{"operationId":"list_subjects","description":"List all registered subjects in the schema registry.","summary":"Subjects: ${result.result}","responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions":{"get":{"operationId":"describe_subject","description":"List the schema version numbers registered for a subject.","summary":"Versions: ${result.result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"operationId":"register_schema","description":"Register a new schema version under a subject.","summary":"Registered schema with id ${result.id}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions/{version}":{"get":{"operationId":"get_schema","description":"Retrieve a specific registered schema version for a subject.","summary":"Retrieved schema id ${result.id}, version ${result.version}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"delete":{"operationId":"delete_schema_version","description":"Delete a specific registered schema version for a subject.","summary":"Deleted version ${result.result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}":{"delete":{"operationId":"delete_subject","description":"Delete a subject and all its registered schema versions.","summary":"Deleted versions: ${result.result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/compatibility/subjects/{subject}/versions/{version}":{"post":{"operationId":"check_compatibility","description":"Check whether a given schema is compatible with the existing versions registered for a subject.","summary":"Compatibility check result: ${result.is_compatible}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/config/{subject}":{"get":{"operationId":"get_compatibility","description":"Get the compatibility level configured for a subject.","summary":"Compatibility level is ${result.compatibilityLevel}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"operationId":"set_compatibility","description":"Set the compatibility level for a subject.","summary":"Compatibility level set to ${result.compatibility}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"compatibility":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}}}} \ No newline at end of file diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml index e745a557047..fed0538b217 100644 --- a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/client.yaml @@ -212,6 +212,13 @@ catalogs: "type": "object", "properties": {} } + get_dashboard_params: + version: latest + schema: | + { + "type": "object", + "properties": {} + } echo_id_params: version: latest schema: | @@ -379,7 +386,7 @@ bindings: list_tags: description: > List repository tags. - summary: "done" + summary: "Tags: ${result}" schemas: input: model: json @@ -422,6 +429,19 @@ bindings: http_github0: - subject: get_status_params version: latest + get_dashboard: + description: > + Get a status dashboard. A bare-object response with no declared output schema, covering + tool.summary's container capture: ${result.data} re-serializes a whole nested object as + compact JSON, alongside bare scalar/null/boolean leaf references. + summary: "Dashboard: ${result.data} active=${result.active} disabled=${result.disabled} flag=${result.flag}" + schemas: + input: + model: json + catalog: + http_github0: + - subject: get_dashboard_params + version: latest echo_id: description: > Echo the given identifier. @@ -600,6 +620,14 @@ bindings: ":scheme": https ":authority": api.github.com:443 ":path": /status + - when: + - tool: get_dashboard + with: + headers: + ":method": GET + ":scheme": https + ":authority": api.github.com:443 + ":path": /dashboard - when: - tool: echo_id with: diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml index 4144171c946..8c605217fe2 100644 --- a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/config/proxy.yaml @@ -212,6 +212,13 @@ catalogs: "type": "object", "properties": {} } + get_dashboard_params: + version: latest + schema: | + { + "type": "object", + "properties": {} + } echo_id_params: version: latest schema: | @@ -379,7 +386,7 @@ bindings: list_tags: description: > List repository tags. - summary: "done" + summary: "Tags: ${result}" schemas: input: model: json @@ -422,6 +429,19 @@ bindings: http_github0: - subject: get_status_params version: latest + get_dashboard: + description: > + Get a status dashboard. A bare-object response with no declared output schema, covering + tool.summary's container capture: ${result.data} re-serializes a whole nested object as + compact JSON, alongside bare scalar/null/boolean leaf references. + summary: "Dashboard: ${result.data} active=${result.active} disabled=${result.disabled} flag=${result.flag}" + schemas: + input: + model: json + catalog: + http_github0: + - subject: get_dashboard_params + version: latest echo_id: description: > Echo the given identifier. @@ -610,6 +630,15 @@ bindings: ":scheme": https ":authority": api.github.com:443 ":path": /status + - when: + - tool: get_dashboard + exit: http0 + with: + headers: + ":method": GET + ":scheme": https + ":authority": api.github.com:443 + ":path": /dashboard - when: - tool: echo_id exit: http0 diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/client.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/client.rpt new file mode 100644 index 00000000000..fc5d79e3d6c --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/client.rpt @@ -0,0 +1,40 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +connect "zilla://streams/http0" + option zilla:window 8192 + option zilla:transmission "half-duplex" + +write zilla:begin.ext ${http:beginEx() + .typeId(zilla:id("http")) + .header(":method", "GET") + .header(":scheme", "https") + .header(":authority", "api.github.com:443") + .header(":path", "/dashboard") + .build()} + +connected + +read zilla:begin.ext ${http:matchBeginEx() + .typeId(zilla:id("http")) + .header(":status", "200") + .header("content-type", "application/json") + .build()} + +read '{"active":true,"disabled":false,"flag":null,"data":{"name":"Ops \\"Team\\"",' + '"count":3,"verified":true,"archived":false,"note":null,"tags":["a","b"]}}' +read closed + +write close diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/server.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/server.rpt new file mode 100644 index 00000000000..e7973d941f3 --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/http/get.dashboard/server.rpt @@ -0,0 +1,47 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +property serverAddress "zilla://streams/http0" + +accept ${serverAddress} + option zilla:window 8192 + option zilla:transmission "half-duplex" + +accepted + +read zilla:begin.ext ${http:matchBeginEx() + .typeId(zilla:id("http")) + .header(":method", "GET") + .header(":scheme", "https") + .header(":authority", "api.github.com:443") + .header(":path", "/dashboard") + .build()} + +connected + +write zilla:begin.ext ${http:beginEx() + .typeId(zilla:id("http")) + .header(":status", "200") + .header("content-type", "application/json") + .build()} +write flush + +write '{"active":true,"disabled":false,"flag":null,"data":{"name":"Ops \\"Team\\"",' + '"count":3,"verified":true,"archived":false,"note":null,"tags":["a","b"]}}' +write flush + +write close + +read closed diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/client.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/client.rpt new file mode 100644 index 00000000000..9dfc6925d22 --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/client.rpt @@ -0,0 +1,64 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +connect "zilla://streams/app0" + option zilla:window 8192 + option zilla:transmission "half-duplex" + +write zilla:begin.ext ${mcp:beginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .build() + .build()} + +connected + +read zilla:begin.ext ${mcp:matchBeginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .build() + .build()} + +read notify LIFECYCLE_INITIALIZED + +connect await LIFECYCLE_INITIALIZED + "zilla://streams/app0" + option zilla:window 8192 + option zilla:transmission "half-duplex" + +write zilla:begin.ext ${mcp:beginEx() + .typeId(zilla:id("mcp")) + .toolsCall() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .name("get_dashboard") + .contentLength(39) + .build() + .build()} + +connected + +write '{"name":"get_dashboard","arguments":{}}' + +# tool has no output schema, so the raw upstream body streams through unprojected. tool.summary +# references ${result.data} (an object nested under a key, re-serialized as compact JSON by +# McpHttpResults' container capture, including a quote character that must round-trip through +# JSON escaping) alongside three top-level scalar leaf references -- ${result.active} (true), +# ${result.disabled} (false), and ${result.flag} (null) -- covering every scalar/container +# combination captureContainer and the leaf VALUE_TRUE/VALUE_FALSE/VALUE_NULL branches handle +read '{"structuredContent":{"active":true,"disabled":false,"flag":null,"data":{"name":"Ops \\"Team\\"","count":3,"verified":true,"archived":false,"note":null,"tags":["a","b"]}},"content":[{"type":"text","text":"Dashboard: {\\"name\\":\\"Ops \\\\\\"Team\\\\\\"\\",\\"count\\":3,\\"verified\\":true,\\"archived\\":false,\\"note\\":null,\\"tags\\":[\\"a\\",\\"b\\"]} active=true disabled=false flag=null"}],"isError":false}' +read closed + +write close diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/server.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/server.rpt new file mode 100644 index 00000000000..f4d7de1292d --- /dev/null +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/get.dashboard/server.rpt @@ -0,0 +1,62 @@ +# +# Copyright 2021-2026 Aklivity Inc +# +# Licensed under the Aklivity Community License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy of the +# License at +# +# https://www.aklivity.io/aklivity-community-license/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# + +property serverAddress "zilla://streams/app0" + +accept ${serverAddress} + option zilla:window 8192 + option zilla:transmission "half-duplex" + +accepted + +read zilla:begin.ext ${mcp:matchBeginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .build() + .build()} + +connected + +write zilla:begin.ext ${mcp:beginEx() + .typeId(zilla:id("mcp")) + .lifecycle() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .build() + .build()} +write flush + +accepted + +read zilla:begin.ext ${mcp:matchBeginEx() + .typeId(zilla:id("mcp")) + .toolsCall() + .sessionId("5ca1ab1e-c0de-4a11-5e55-000100000000") + .name("get_dashboard") + .contentLength(39) + .build() + .build()} + +connected + +read '{"name":"get_dashboard","arguments":{}}' + +write flush + +write '{"structuredContent":{"active":true,"disabled":false,"flag":null,"data":{"name":"Ops \\"Team\\"","count":3,"verified":true,"archived":false,"note":null,"tags":["a","b"]}},"content":[{"type":"text","text":"Dashboard: {\\"name\\":\\"Ops \\\\\\"Team\\\\\\"\\",\\"count\\":3,\\"verified\\":true,\\"archived\\":false,\\"note\\":null,\\"tags\\":[\\"a\\",\\"b\\"]} active=true disabled=false flag=null"}],"isError":false}' +write flush + +write close + +read closed diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/client.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/client.rpt index c1d25631d72..7ba8649f417 100644 --- a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/client.rpt +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/client.rpt @@ -53,10 +53,12 @@ connected write '{"name":"list_tags","arguments":{}}' # tool.output (list_tags_result) is an array-rooted schema: structuredContent is the array itself, not -# an object wrapping it — proves McpHttpToolResult's array-root envelope step +# an object wrapping it — proves McpHttpToolResult's array-root envelope step. tool.summary's bare +# ${result} (no path) captures that same root array, re-serialized as compact JSON, proving +# McpHttpResults' container capture alongside its existing scalar-only capture read '{' '"structuredContent":["v1","v2","v3"],' - '"content":[{"type":"text","text":"done"}],' + '"content":[{"type":"text","text":"Tags: [\\"v1\\",\\"v2\\",\\"v3\\"]"}],' '"isError":false' '}' read closed diff --git a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/server.rpt b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/server.rpt index 9f46ca514f8..fa87c22a777 100644 --- a/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/server.rpt +++ b/specs/binding-mcp-http.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/http/streams/mcp/list.tags/server.rpt @@ -56,7 +56,7 @@ write flush write '{' '"structuredContent":["v1","v2","v3"],' - '"content":[{"type":"text","text":"done"}],' + '"content":[{"type":"text","text":"Tags: [\\"v1\\",\\"v2\\",\\"v3\\"]"}],' '"isError":false' '}' write flush diff --git a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java index f0bf0123f93..36e66630dd8 100644 --- a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java +++ b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/HttpClientIT.java @@ -234,6 +234,15 @@ public void shouldProxyGetStatusToHttp() throws Exception k3po.finish(); } + @Test + @Specification({ + "${http}/get.dashboard/client", + "${http}/get.dashboard/server"}) + public void shouldProxyGetDashboardToHttp() throws Exception + { + k3po.finish(); + } + @Test @Specification({ "${http}/echo.id.large/client", diff --git a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java index 72bc101bd4b..476b495f706 100644 --- a/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java +++ b/specs/binding-mcp-http.spec/src/test/java/io/aklivity/zilla/specs/binding/mcp/http/streams/McpServerIT.java @@ -324,6 +324,15 @@ public void shouldCallToolGetStatus() throws Exception k3po.finish(); } + @Test + @Specification({ + "${mcp}/get.dashboard/client", + "${mcp}/get.dashboard/server"}) + public void shouldCallToolGetDashboard() throws Exception + { + k3po.finish(); + } + @Test @Specification({ "${mcp}/echo.id.large/client", diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt index 03455802cf4..df078ad1bd6 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connector_plugins","arguments":{}}' -read '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Listed connector plugins"}],"isError":false}' +read '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt index 6fe3179fec6..9d9a5b0b88f 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connector_plugins","arguments":{}}' write flush -write '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Listed connector plugins"}],"isError":false}' +write '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt index eb2d1f7572d..91cd3044ad4 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connector_tasks","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Listed connector tasks"}],"isError":false}' +read '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt index 676a40a394e..439a71558e4 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connector_tasks","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Listed connector tasks"}],"isError":false}' +write '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt index 73fa6ff803e..5a5b8b949c6 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connectors","arguments":{}}' -read '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Listed connectors"}],"isError":false}' +read '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt index 5f86ee7e5a2..404a2e346e0 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connectors","arguments":{}}' write flush -write '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Listed connectors"}],"isError":false}' +write '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt index 373dfdd82de..e87867e55ee 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_schema_version","arguments":{"subject":"orders-value","version":"1"}}' -read '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Delete a specific schema version"}],"isError":false}' +read '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt index 2e81a1af9db..1a3a92ca872 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_schema_version","arguments":{"subject":"orders-value","ve write flush -write '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Delete a specific schema version"}],"isError":false}' +write '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt index d2e4dc942c5..15f6ebc06b9 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_subject","arguments":{"subject":"orders-value"}}' -read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Delete a subject and all its versions"}],"isError":false}' +read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt index 4eaa11b0eb4..68ea3b7d3b0 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_subject","arguments":{"subject":"orders-value"}}' write flush -write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Delete a subject and all its versions"}],"isError":false}' +write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt index ed2f6518fd8..ca57e7194b8 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"describe_subject","arguments":{"subject":"orders-value"}}' -read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"List versions for a subject"}],"isError":false}' +read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt index c22c98f668c..99cabff3f86 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt @@ -54,7 +54,7 @@ read '{"name":"describe_subject","arguments":{"subject":"orders-value"}}' write flush -write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"List versions for a subject"}],"isError":false}' +write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt index b8393c61fba..12dbf7d3727 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_subjects","arguments":{}}' -read '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"List subjects"}],"isError":false}' +read '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt index c8ccfec8e6b..975de2d6c32 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_subjects","arguments":{}}' write flush -write '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"List subjects"}],"isError":false}' +write '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' write flush write close From 3ee5befea51a093ea7bde795dda75348cf2f1f33 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 03:50:32 +0000 Subject: [PATCH 4/6] fix(binding-mcp-openapi): only wrap structuredContent when a schema demands it outputMaybeWrapped treated "no declared response schema at all" the same as "a declared response schema that isn't object-typed" -- both made hasObjectOutputSchema() return false. Only the second case is a real constraint: MCP's outputSchema (and the structuredContent it describes) must be an object, so a non-object *advertised* schema needs the response body wrapped to match it. When no response schema is declared at all, no outputSchema is ever advertised (the derived "-output" catalog subject is only registered when the OpenAPI operation's response actually declares one) -- so there is nothing for structuredContent to conform to, and wrapping served no purpose beyond its own side effect of hiding the real root value from a summary template's bare ${result} behind a synthetic {"result": } envelope. Add hasDeclaredOutputSchema() as an explicit precondition alongside the existing object-schema check, so a tool with no declared response schema is no longer wrapped and a bare ${result} in its tool.summary addresses the real response. Restores the seven binding-mcp-schema-registry and binding-mcp-kafka-connect tool.summary strings updated in the prior commit to plain ${result} instead of the ${result.result} workaround their wrapping required. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AvsVDMfv1iedqo667q74JT --- .../schema/kafka-connect.openapi.json | 6 +- .../McpOpenapiCompositeGenerator.java | 26 ++++++-- .../McpOpenapiCompositeGeneratorTest.java | 59 +++++++++++++++++++ .../karapace-schema-registry.openapi.json | 2 +- .../mcp/list.connector.plugins/client.rpt | 2 +- .../mcp/list.connector.plugins/server.rpt | 2 +- .../mcp/list.connector.tasks/client.rpt | 2 +- .../mcp/list.connector.tasks/server.rpt | 2 +- .../streams/mcp/list.connectors/client.rpt | 2 +- .../streams/mcp/list.connectors/server.rpt | 2 +- .../mcp/delete.schema.version/client.rpt | 2 +- .../mcp/delete.schema.version/server.rpt | 2 +- .../streams/mcp/delete.subject/client.rpt | 2 +- .../streams/mcp/delete.subject/server.rpt | 2 +- .../streams/mcp/describe.subject/client.rpt | 2 +- .../streams/mcp/describe.subject/server.rpt | 2 +- .../streams/mcp/list.subjects/client.rpt | 2 +- .../streams/mcp/list.subjects/server.rpt | 2 +- 18 files changed, 97 insertions(+), 24 deletions(-) diff --git a/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json b/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json index df91d752d7f..e0181aac537 100644 --- a/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json +++ b/runtime/binding-mcp-kafka-connect/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/kafka/connect/internal/schema/kafka-connect.openapi.json @@ -13,7 +13,7 @@ "/connectors": { "get": { "operationId": "list_connectors", - "summary": "Connectors: ${result.result}", + "summary": "Connectors: ${result}", "responses": { "200": { "description": "ok" @@ -303,7 +303,7 @@ "/connectors/{connector}/tasks": { "get": { "operationId": "list_connector_tasks", - "summary": "Tasks: ${result.result}", + "summary": "Tasks: ${result}", "parameters": [ { "name": "connector", @@ -452,7 +452,7 @@ "/connector-plugins": { "get": { "operationId": "list_connector_plugins", - "summary": "Plugins: ${result.result}", + "summary": "Plugins: ${result}", "responses": { "200": { "description": "ok" diff --git a/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java b/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java index 725694bd0a8..1e9a58d2f67 100644 --- a/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java +++ b/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java @@ -420,7 +420,9 @@ private McpHttpOptionsConfig mcpHttpOptions( if (entry.tool != null) { - final boolean outputMaybeWrapped = entry.tool.output == null && !hasObjectOutputSchema(entry.operation); + final boolean outputMaybeWrapped = entry.tool.output == null && + hasDeclaredOutputSchema(entry.operation) && + !hasObjectOutputSchema(entry.operation); final ModelConfig output = entry.tool.output != null ? qualifyModel(binding, entry.tool.output) : jsonModel("%s-output".formatted(name)); @@ -1067,12 +1069,24 @@ private static OpenapiResponseView successResponse( return result; } + // Whether the OpenAPI operation declares any response schema at all, object-typed or not -- the + // precondition for outputMaybeWrapped below. An operation with no declared response schema never gets + // an advertised outputSchema in the first place (the derived "-output" catalog subject is only + // ever registered inside the success.content != null branch above), so there is nothing for + // structuredContent to conform to and wrapping it would serve no protocol purpose. + private static boolean hasDeclaredOutputSchema( + OpenapiOperationView operation) + { + final OpenapiResponseView success = successResponse(operation); + return success != null && success.content != null; + } + // MCP's tool outputSchema (and the structuredContent it describes) must be a JSON object; an OpenAPI - // response whose own schema is array- or primitive-typed is wrapped as {"result": } instead - // of advertised as-is -- see wrapAsObjectSchema and McpHttpResultWrap, which wraps the real response - // body the same way so structuredContent still matches the advertised schema. A resource's own output - // schema has no such constraint (it is never advertised, only used to project the read body), so this - // check and the wrapping it drives applies to tools only + // response whose own declared schema is array- or primitive-typed is wrapped as {"result": } + // instead of advertised as-is -- see wrapAsObjectSchema and McpHttpResultWrap, which wraps the real + // response body the same way so structuredContent still matches the advertised schema. A resource's + // own output schema has no such constraint (it is never advertised, only used to project the read + // body), so this check and the wrapping it drives applies to tools only private static boolean hasObjectOutputSchema( OpenapiOperationView operation) { diff --git a/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java b/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java index 693189c5baa..9fdc65e1afc 100644 --- a/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java +++ b/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java @@ -306,6 +306,15 @@ public class McpOpenapiCompositeGeneratorTest "items": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" } } } } } } } } } + }, + "/pets/{id}": { + "delete": { + "operationId": "delete_pet", + "parameters": [ + { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } + ], + "responses": { "200": { "description": "ok" } } + } } } } @@ -2931,6 +2940,56 @@ public void shouldNotWrapExplicitOutputOverrideEvenForArrayTypedResponse() assertThat(tool.outputMaybeWrapped, equalTo(false)); } + @Test + public void shouldNotWrapWhenNoOutputSchemaDeclared() + { + lenient().when(catalog.resolve(eq("petstore-api"), eq("latest"))).thenReturn(66); + lenient().when(catalog.resolve(eq(66))).thenReturn(PETSTORE_SPEC); + + BindingConfig binding = GenericBindingConfig.builder() + .namespace("test") + .name("mcp-openapi0") + .type("mcp-openapi") + .kind(CLIENT) + .options(McpOpenapiOptionsConfig.builder() + .spec() + .label("petstore") + .server("https://api.petstore.example.com") + .catalog() + .name("catalog0") + .subject("petstore-api") + .version("latest") + .build() + .build() + .build()) + .route() + .when(McpOpenapiConditionConfig.builder() + .tool("delete_pet") + .build()) + .with(McpOpenapiWithConfig.builder() + .spec("petstore") + .operation("delete_pet") + .build()) + .build() + .build(); + binding.resolveId = resolveId; + + McpOpenapiCompositeConfig composite = generator.generate(new McpOpenapiBindingConfig(context, binding)); + + BindingConfig mcpHttp = composite.namespaces.get(0).bindings.stream() + .filter(b -> "mcp-http0".equals(b.name)) + .findFirst() + .orElse(null); + McpHttpOptionsConfig mcpHttpOptions = (McpHttpOptionsConfig) mcpHttp.options; + McpHttpToolConfig tool = mcpHttpOptions.tools.stream() + .filter(t -> "delete_pet".equals(t.name)) + .findFirst() + .orElse(null); + + assertThat(tool, notNullValue()); + assertThat(tool.outputMaybeWrapped, equalTo(false)); + } + @Test public void shouldDeriveToolAnnotationsFromHttpMethod() { diff --git a/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json b/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json index 0ac3afafc8b..fd685e7623d 100644 --- a/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json +++ b/runtime/binding-mcp-schema-registry/src/main/resources/io/aklivity/zilla/runtime/binding/mcp/schema/registry/internal/schema/karapace-schema-registry.openapi.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"Schema Registry API","version":"1.0"},"servers":[{"url":"http://localhost:8080"}],"paths":{"/subjects":{"get":{"operationId":"list_subjects","description":"List all registered subjects in the schema registry.","summary":"Subjects: ${result.result}","responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions":{"get":{"operationId":"describe_subject","description":"List the schema version numbers registered for a subject.","summary":"Versions: ${result.result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"operationId":"register_schema","description":"Register a new schema version under a subject.","summary":"Registered schema with id ${result.id}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions/{version}":{"get":{"operationId":"get_schema","description":"Retrieve a specific registered schema version for a subject.","summary":"Retrieved schema id ${result.id}, version ${result.version}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"delete":{"operationId":"delete_schema_version","description":"Delete a specific registered schema version for a subject.","summary":"Deleted version ${result.result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}":{"delete":{"operationId":"delete_subject","description":"Delete a subject and all its registered schema versions.","summary":"Deleted versions: ${result.result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/compatibility/subjects/{subject}/versions/{version}":{"post":{"operationId":"check_compatibility","description":"Check whether a given schema is compatible with the existing versions registered for a subject.","summary":"Compatibility check result: ${result.is_compatible}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/config/{subject}":{"get":{"operationId":"get_compatibility","description":"Get the compatibility level configured for a subject.","summary":"Compatibility level is ${result.compatibilityLevel}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"operationId":"set_compatibility","description":"Set the compatibility level for a subject.","summary":"Compatibility level set to ${result.compatibility}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"compatibility":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"Schema Registry API","version":"1.0"},"servers":[{"url":"http://localhost:8080"}],"paths":{"/subjects":{"get":{"operationId":"list_subjects","description":"List all registered subjects in the schema registry.","summary":"Subjects: ${result}","responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions":{"get":{"operationId":"describe_subject","description":"List the schema version numbers registered for a subject.","summary":"Versions: ${result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"operationId":"register_schema","description":"Register a new schema version under a subject.","summary":"Registered schema with id ${result.id}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}/versions/{version}":{"get":{"operationId":"get_schema","description":"Retrieve a specific registered schema version for a subject.","summary":"Retrieved schema id ${result.id}, version ${result.version}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"delete":{"operationId":"delete_schema_version","description":"Delete a specific registered schema version for a subject.","summary":"Deleted version ${result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/subjects/{subject}":{"delete":{"operationId":"delete_subject","description":"Delete a subject and all its registered schema versions.","summary":"Deleted versions: ${result}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/compatibility/subjects/{subject}/versions/{version}":{"post":{"operationId":"check_compatibility","description":"Check whether a given schema is compatible with the existing versions registered for a subject.","summary":"Compatibility check result: ${result.is_compatible}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"type":"string"},"schemaType":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}},"/config/{subject}":{"get":{"operationId":"get_compatibility","description":"Get the compatibility level configured for a subject.","summary":"Compatibility level is ${result.compatibilityLevel}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"operationId":"set_compatibility","description":"Set the compatibility level for a subject.","summary":"Compatibility level set to ${result.compatibility}","parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"compatibility":{"type":"string"}}}}}},"responses":{"200":{"description":"ok"}}}}}} \ No newline at end of file diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt index df078ad1bd6..7c660447e70 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connector_plugins","arguments":{}}' -read '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' +read '{"structuredContent":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}],"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt index 9d9a5b0b88f..6d90a1be23c 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connector_plugins","arguments":{}}' write flush -write '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' +write '{"structuredContent":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}],"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt index 91cd3044ad4..b3fe18f6447 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connector_tasks","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' +read '{"structuredContent":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}],"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt index 439a71558e4..982401e9539 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connector_tasks","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' +write '{"structuredContent":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}],"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt index 5a5b8b949c6..81d893607b6 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connectors","arguments":{}}' -read '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' +read '{"structuredContent":["connector1","connector2"],"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt index 404a2e346e0..56547bb4abb 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connectors","arguments":{}}' write flush -write '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' +write '{"structuredContent":["connector1","connector2"],"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt index e87867e55ee..43aef05e650 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_schema_version","arguments":{"subject":"orders-value","version":"1"}}' -read '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' +read '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt index 1a3a92ca872..2d042688b7f 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_schema_version","arguments":{"subject":"orders-value","ve write flush -write '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' +write '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt index 15f6ebc06b9..91f0b463d29 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_subject","arguments":{"subject":"orders-value"}}' -read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' +read '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt index 68ea3b7d3b0..063dc9279e2 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_subject","arguments":{"subject":"orders-value"}}' write flush -write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' +write '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt index ca57e7194b8..cdfae4a0d21 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"describe_subject","arguments":{"subject":"orders-value"}}' -read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' +read '{"structuredContent":[1,2],"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt index 99cabff3f86..15fccb6ce36 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt @@ -54,7 +54,7 @@ read '{"name":"describe_subject","arguments":{"subject":"orders-value"}}' write flush -write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' +write '{"structuredContent":[1,2],"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt index 12dbf7d3727..eed4d8be791 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_subjects","arguments":{}}' -read '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' +read '{"structuredContent":["orders-value","payments-value"],"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt index 975de2d6c32..ff9dd790187 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_subjects","arguments":{}}' write flush -write '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' +write '{"structuredContent":["orders-value","payments-value"],"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' write flush write close From 89e032d43f4bc7ebad1800dc9cc25d32b09f560b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 03:57:39 +0000 Subject: [PATCH 5/6] fix: unwrap structuredContent for more no-schema kafka-connect/schema-registry tools Merging develop brought in new tests written against the pre-fix wrapping behavior: two schema-registry "permanent delete" script variants, and six kafka-connect operations (pause/stop/restart/resume/ delete connector, restart connector task) whose empty response bodies parse to JSON null. All eight have no declared response schema, so the prior commit's outputMaybeWrapped fix un-wraps them the same way it did the original seven -- their structuredContent is now the real value (null, or the real array/scalar for the two permanent-delete scripts) instead of a synthetic {"result": } envelope. Update their expected structuredContent to match; none of these eight reference ${result...} in their summary, so no summary text changes. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AvsVDMfv1iedqo667q74JT --- .../mcp/kafka/connect/streams/mcp/delete.connector/client.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/delete.connector/server.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/pause.connector/client.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/pause.connector/server.rpt | 2 +- .../kafka/connect/streams/mcp/restart.connector.task/client.rpt | 2 +- .../kafka/connect/streams/mcp/restart.connector.task/server.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/restart.connector/client.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/restart.connector/server.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/resume.connector/client.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/resume.connector/server.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/stop.connector/client.rpt | 2 +- .../mcp/kafka/connect/streams/mcp/stop.connector/server.rpt | 2 +- .../streams/mcp/delete.schema.version.permanent/client.rpt | 2 +- .../streams/mcp/delete.schema.version.permanent/server.rpt | 2 +- .../registry/streams/mcp/delete.subject.permanent/client.rpt | 2 +- .../registry/streams/mcp/delete.subject.permanent/server.rpt | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt index ba75a3bac4a..6a94e1cd1a0 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' +read '{"structuredContent":null,"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt index 4078d56f0ab..24218769a79 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' +write '{"structuredContent":null,"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt index 629ece893cc..bcd272cc629 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"pause_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Paused connector"}],"isError":false}' +read '{"structuredContent":null,"content":[{"type":"text","text":"Paused connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt index 9ca4bdcac37..3148a974649 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"pause_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Paused connector"}],"isError":false}' +write '{"structuredContent":null,"content":[{"type":"text","text":"Paused connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt index 49ce05fb424..88fe96d834a 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"restart_connector_task","arguments":{"connector":"connector1","task":0}}' -read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' +read '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt index e415e5937c8..a405eae5814 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt @@ -54,7 +54,7 @@ read '{"name":"restart_connector_task","arguments":{"connector":"connector1","t write flush -write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' +write '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt index eb260c76b70..64614ef2750 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"restart_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' +read '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt index 36444709b5a..ba8267c43a9 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"restart_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' +write '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt index 9f2f5a9da60..9d9a303657a 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"resume_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' +read '{"structuredContent":null,"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt index c34bfd9231d..a8309a3641d 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"resume_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' +write '{"structuredContent":null,"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt index 54a8e15bd80..85d05996ee5 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"stop_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' +read '{"structuredContent":null,"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt index 70dd3258306..474cea8d795 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"stop_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' +write '{"structuredContent":null,"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt index 96d4ed083bb..05b4794de37 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_schema_version","arguments":{"subject":"orders-value","version":"1","permanent":true}}' -read '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Delete a specific schema version"}],"isError":false}' +read '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt index 76b979e4e43..dfc730e3cb1 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_schema_version","arguments":{"subject":"orders-value","ve write flush -write '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Delete a specific schema version"}],"isError":false}' +write '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt index 434a84bd7e9..e3f0bd5a904 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_subject","arguments":{"subject":"orders-value","permanent":true}}' -read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Delete a subject and all its versions"}],"isError":false}' +read '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt index ebcaf0f7f96..687cda52763 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_subject","arguments":{"subject":"orders-value","permanent write flush -write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Delete a subject and all its versions"}],"isError":false}' +write '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' write flush write close From dd737d120f700c7d0a12f18b6bf6f818d6a06354 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 04:56:56 +0000 Subject: [PATCH 6/6] fix(binding-mcp-openapi): restore structuredContent wrapping for MCP protocol compliance The prior commit's outputMaybeWrapped fix unwrapped structuredContent for any tool with no declared response schema, on the theory that wrapping served no purpose without an advertised outputSchema to conform to. That overlooked a harder constraint: the MCP protocol itself requires structuredContent to always be a JSON object (confirmed via the official TypeScript SDK's CallToolResultSchema, which types structuredContent as z.record(...)). An undeclared-schema response can still be a bare array or scalar at runtime, so leaving it unwrapped produces a response real MCP clients reject outright -- exactly what broke the mcp.proxy example's kafka_sr__list_subjects call in CI. Revert outputMaybeWrapped to wrap whenever the response isn't provably an object (declared or not), and remove hasDeclaredOutputSchema along with it. Fix the actual awkwardness -- a wrapped tool's tool.summary needing ${result.result} instead of ${result} -- at the layer that owns it instead: McpHttpProxyFactory now tracks whether the active tool is wrapped and transparently resolves a bare ${result} against the wrap envelope's "result" key rather than the streamed root, so the wrapper stays invisible to summary authors without touching the wire format. newToolResultReferences remaps the same bare capture path so McpHttpResults captures the right key. Restores the corresponding binding-mcp-schema-registry and binding-mcp-kafka-connect spec scripts to expect wrapped structuredContent (matching the protocol-correct behavior) while keeping their tool.summary text on the clean, unwrapped ${result} value. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AvsVDMfv1iedqo667q74JT --- .../internal/stream/McpHttpProxyFactory.java | 24 ++++++++++++--- .../McpOpenapiCompositeGenerator.java | 29 +++++-------------- .../McpOpenapiCompositeGeneratorTest.java | 4 +-- .../streams/mcp/delete.connector/client.rpt | 2 +- .../streams/mcp/delete.connector/server.rpt | 2 +- .../mcp/list.connector.plugins/client.rpt | 2 +- .../mcp/list.connector.plugins/server.rpt | 2 +- .../mcp/list.connector.tasks/client.rpt | 2 +- .../mcp/list.connector.tasks/server.rpt | 2 +- .../streams/mcp/list.connectors/client.rpt | 2 +- .../streams/mcp/list.connectors/server.rpt | 2 +- .../streams/mcp/pause.connector/client.rpt | 2 +- .../streams/mcp/pause.connector/server.rpt | 2 +- .../mcp/restart.connector.task/client.rpt | 2 +- .../mcp/restart.connector.task/server.rpt | 2 +- .../streams/mcp/restart.connector/client.rpt | 2 +- .../streams/mcp/restart.connector/server.rpt | 2 +- .../streams/mcp/resume.connector/client.rpt | 2 +- .../streams/mcp/resume.connector/server.rpt | 2 +- .../streams/mcp/stop.connector/client.rpt | 2 +- .../streams/mcp/stop.connector/server.rpt | 2 +- .../client.rpt | 2 +- .../server.rpt | 2 +- .../mcp/delete.schema.version/client.rpt | 2 +- .../mcp/delete.schema.version/server.rpt | 2 +- .../mcp/delete.subject.permanent/client.rpt | 2 +- .../mcp/delete.subject.permanent/server.rpt | 2 +- .../streams/mcp/delete.subject/client.rpt | 2 +- .../streams/mcp/delete.subject/server.rpt | 2 +- .../streams/mcp/describe.subject/client.rpt | 2 +- .../streams/mcp/describe.subject/server.rpt | 2 +- .../streams/mcp/list.subjects/client.rpt | 2 +- .../streams/mcp/list.subjects/server.rpt | 2 +- 33 files changed, 60 insertions(+), 57 deletions(-) diff --git a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java index 54c28f03d3a..aaeaaa2406f 100644 --- a/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java +++ b/runtime/binding-mcp-http/src/main/java/io/aklivity/zilla/runtime/binding/mcp/http/internal/stream/McpHttpProxyFactory.java @@ -1001,6 +1001,12 @@ private final class McpToolsCallProxy extends McpHttpProxy // response completes to resolve tool.summary's ${result.*} references without re-scanning a buffer private final Map capturedResults = new HashMap<>(); + // true when this tool's structuredContent is McpHttpResultWrap-wrapped as {"result":} to satisfy + // the MCP wire contract that structuredContent is always a JSON object; a bare ${result} then means the + // real (unwrapped) response value, so it is resolved against the captured "result" key rather than the + // captured root -- keeping the wrapper an implementation detail invisible to tool.summary authors + private boolean resultWrapped; + // the non-2xx response mode: relays the raw upstream body as escaped text with no JsonPipeline at all // (the body is not guaranteed to be valid JSON), using errorGenerator directly the same way responseStep // uses responseGenerator — wrap against encodeSlot's live position, drive via consumed()/length() @@ -1353,9 +1359,10 @@ void responseBegin( final String summaryTemplate = tool != null ? tool.summary : null; final List resultPaths = tool != null ? toolResultReferences(tool) : List.of(); + resultWrapped = tool != null && tool.outputMaybeWrapped; responseGenerator = JsonEx.createGenerator(); JsonStream stream = JsonEx.stream(JsonEx.createParser()); - if (tool != null && tool.outputMaybeWrapped) + if (resultWrapped) { // nothing proves the upstream body is already an object; route it through the // transform that decides, from the real body's own first event, whether it actually @@ -1455,14 +1462,16 @@ private JsonPipeline.Status errorRelayStep( // Resolves a result. reference, or a bare result reference (the response's own root value), // from the values McpHttpResults captured while structuredContent streamed past, replacing a - // re-scan of a fully buffered response copy. + // re-scan of a fully buffered response copy. When resultWrapped, the captured root is the + // McpHttpResultWrap envelope rather than the real value, so a bare ${result} is redirected to the + // captured "result" key instead -- see resultWrapped's field doc. private String resolveCapturedResult( String expression) { String value = ""; if ("result".equals(expression) || expression.startsWith("result.")) { - final String path = "result".equals(expression) ? "" : expression.substring(7); + final String path = "result".equals(expression) ? (resultWrapped ? "result" : "") : expression.substring(7); final String captured = capturedResults.get(path); value = captured != null ? captured : ""; } @@ -2400,7 +2409,14 @@ private List toolResultReferences( private List newToolResultReferences( McpHttpToolConfig tool) { - return resultReferences(tool.summary); + final List paths = resultReferences(tool.summary); + if (tool.outputMaybeWrapped) + { + // the streamed root is the McpHttpResultWrap envelope, not the real value -- capture "result" + // (the envelope's own value key) in place of a bare root capture, matching resolveCapturedResult + paths.replaceAll(path -> path.isEmpty() ? "result" : path); + } + return paths; } private void appendQuery( diff --git a/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java b/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java index 29ebcf406e8..e930e223e8a 100644 --- a/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java +++ b/runtime/binding-mcp-openapi/src/main/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGenerator.java @@ -420,9 +420,7 @@ private McpHttpOptionsConfig mcpHttpOptions( if (entry.tool != null) { - final boolean outputMaybeWrapped = entry.tool.output == null && - hasDeclaredOutputSchema(entry.operation) && - !hasObjectOutputSchema(entry.operation); + final boolean outputMaybeWrapped = entry.tool.output == null && !hasObjectOutputSchema(entry.operation); final ModelConfig output = entry.tool.output != null ? qualifyModel(binding, entry.tool.output) : jsonModel("%s-output".formatted(name)); @@ -1069,24 +1067,13 @@ private static OpenapiResponseView successResponse( return result; } - // Whether the OpenAPI operation declares any response schema at all, object-typed or not -- the - // precondition for outputMaybeWrapped below. An operation with no declared response schema never gets - // an advertised outputSchema in the first place (the derived "-output" catalog subject is only - // ever registered inside the success.content != null branch above), so there is nothing for - // structuredContent to conform to and wrapping it would serve no protocol purpose. - private static boolean hasDeclaredOutputSchema( - OpenapiOperationView operation) - { - final OpenapiResponseView success = successResponse(operation); - return success != null && success.content != null; - } - - // MCP's tool outputSchema (and the structuredContent it describes) must be a JSON object; an OpenAPI - // response whose own declared schema is array- or primitive-typed is wrapped as {"result": } - // instead of advertised as-is -- see wrapAsObjectSchema and McpHttpResultWrap, which wraps the real - // response body the same way so structuredContent still matches the advertised schema. A resource's - // own output schema has no such constraint (it is never advertised, only used to project the read - // body), so this check and the wrapping it drives applies to tools only + // MCP's structuredContent field must always be a JSON object on the wire, regardless of whether the + // OpenAPI operation declares a response schema at all -- an undeclared-schema response can still be a + // bare array or scalar at runtime, so a response whose own declared (or entirely absent) schema is not + // object-typed is wrapped as {"result": } instead of passed through as-is -- see wrapAsObjectSchema + // and McpHttpResultWrap, which wraps the real response body the same way so structuredContent still + // conforms. A resource's own output schema has no such constraint (it is never advertised, only used to + // project the read body), so this check and the wrapping it drives applies to tools only private static boolean hasObjectOutputSchema( OpenapiOperationView operation) { diff --git a/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java b/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java index 069e8e8f563..e6bb5026a97 100644 --- a/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java +++ b/runtime/binding-mcp-openapi/src/test/java/io/aklivity/zilla/runtime/binding/mcp/openapi/internal/config/composite/McpOpenapiCompositeGeneratorTest.java @@ -3021,7 +3021,7 @@ public void shouldNotWrapExplicitOutputOverrideEvenForArrayTypedResponse() } @Test - public void shouldNotWrapWhenNoOutputSchemaDeclared() + public void shouldWrapWhenNoOutputSchemaDeclared() { lenient().when(catalog.resolve(eq("petstore-api"), eq("latest"))).thenReturn(66); lenient().when(catalog.resolve(eq(66))).thenReturn(PETSTORE_SPEC); @@ -3067,7 +3067,7 @@ public void shouldNotWrapWhenNoOutputSchemaDeclared() .orElse(null); assertThat(tool, notNullValue()); - assertThat(tool.outputMaybeWrapped, equalTo(false)); + assertThat(tool.outputMaybeWrapped, equalTo(true)); } @Test diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt index 6a94e1cd1a0..ba75a3bac4a 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":null,"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' +read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt index 24218769a79..4078d56f0ab 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/delete.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":null,"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' +write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Deleted connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt index 7c660447e70..df078ad1bd6 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connector_plugins","arguments":{}}' -read '{"structuredContent":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}],"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' +read '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt index 6d90a1be23c..9d9a5b0b88f 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.plugins/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connector_plugins","arguments":{}}' write flush -write '{"structuredContent":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}],"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' +write '{"structuredContent":{"result":[{"class":"org.apache.kafka.connect.file.FileStreamSourceConnector","type":"source","version":"1.0"}]},"content":[{"type":"text","text":"Plugins: [{\\"class\\":\\"org.apache.kafka.connect.file.FileStreamSourceConnector\\",\\"type\\":\\"source\\",\\"version\\":\\"1.0\\"}]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt index b3fe18f6447..91cd3044ad4 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connector_tasks","arguments":{"connector":"connector1"}}' -read '{"structuredContent":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}],"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' +read '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt index 982401e9539..439a71558e4 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connector.tasks/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connector_tasks","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}],"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' +write '{"structuredContent":{"result":[{"id":{"connector":"connector1","task":0},"config":{"task.class":"FileStreamSourceTask"}}]},"content":[{"type":"text","text":"Tasks: [{\\"id\\":{\\"connector\\":\\"connector1\\",\\"task\\":0},\\"config\\":{\\"task.class\\":\\"FileStreamSourceTask\\"}}]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt index 81d893607b6..5a5b8b949c6 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_connectors","arguments":{}}' -read '{"structuredContent":["connector1","connector2"],"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' +read '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt index 56547bb4abb..404a2e346e0 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/list.connectors/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_connectors","arguments":{}}' write flush -write '{"structuredContent":["connector1","connector2"],"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' +write '{"structuredContent":{"result":["connector1","connector2"]},"content":[{"type":"text","text":"Connectors: [\\"connector1\\",\\"connector2\\"]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt index bcd272cc629..629ece893cc 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"pause_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":null,"content":[{"type":"text","text":"Paused connector"}],"isError":false}' +read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Paused connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt index 3148a974649..9ca4bdcac37 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/pause.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"pause_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":null,"content":[{"type":"text","text":"Paused connector"}],"isError":false}' +write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Paused connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt index 88fe96d834a..49ce05fb424 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"restart_connector_task","arguments":{"connector":"connector1","task":0}}' -read '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' +read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt index a405eae5814..e415e5937c8 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector.task/server.rpt @@ -54,7 +54,7 @@ read '{"name":"restart_connector_task","arguments":{"connector":"connector1","t write flush -write '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' +write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector task"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt index 64614ef2750..eb260c76b70 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"restart_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' +read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt index ba8267c43a9..36444709b5a 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/restart.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"restart_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":null,"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' +write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Restarted connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt index 9d9a303657a..9f2f5a9da60 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"resume_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":null,"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' +read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt index a8309a3641d..c34bfd9231d 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/resume.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"resume_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":null,"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' +write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Resumed connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt index 85d05996ee5..54a8e15bd80 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"stop_connector","arguments":{"connector":"connector1"}}' -read '{"structuredContent":null,"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' +read '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt index 474cea8d795..70dd3258306 100644 --- a/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt +++ b/specs/binding-mcp-kafka-connect.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/kafka/connect/streams/mcp/stop.connector/server.rpt @@ -54,7 +54,7 @@ read '{"name":"stop_connector","arguments":{"connector":"connector1"}}' write flush -write '{"structuredContent":null,"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' +write '{"structuredContent":{"result":null},"content":[{"type":"text","text":"Stopped connector"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt index 05b4794de37..5718fd7d689 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_schema_version","arguments":{"subject":"orders-value","version":"1","permanent":true}}' -read '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' +read '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt index dfc730e3cb1..8aec7caffde 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version.permanent/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_schema_version","arguments":{"subject":"orders-value","ve write flush -write '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' +write '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt index 43aef05e650..e87867e55ee 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_schema_version","arguments":{"subject":"orders-value","version":"1"}}' -read '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' +read '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt index 2d042688b7f..1a3a92ca872 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.schema.version/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_schema_version","arguments":{"subject":"orders-value","ve write flush -write '{"structuredContent":1,"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' +write '{"structuredContent":{"result":1},"content":[{"type":"text","text":"Deleted version 1"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt index e3f0bd5a904..11b40886e57 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_subject","arguments":{"subject":"orders-value","permanent":true}}' -read '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' +read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt index 687cda52763..00dede49b5f 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject.permanent/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_subject","arguments":{"subject":"orders-value","permanent write flush -write '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' +write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt index 91f0b463d29..15f6ebc06b9 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"delete_subject","arguments":{"subject":"orders-value"}}' -read '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' +read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt index 063dc9279e2..68ea3b7d3b0 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/delete.subject/server.rpt @@ -54,7 +54,7 @@ read '{"name":"delete_subject","arguments":{"subject":"orders-value"}}' write flush -write '{"structuredContent":[1,2],"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' +write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Deleted versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt index cdfae4a0d21..ca57e7194b8 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"describe_subject","arguments":{"subject":"orders-value"}}' -read '{"structuredContent":[1,2],"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' +read '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt index 15fccb6ce36..99cabff3f86 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/describe.subject/server.rpt @@ -54,7 +54,7 @@ read '{"name":"describe_subject","arguments":{"subject":"orders-value"}}' write flush -write '{"structuredContent":[1,2],"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' +write '{"structuredContent":{"result":[1,2]},"content":[{"type":"text","text":"Versions: [1,2]"}],"isError":false}' write flush write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt index eed4d8be791..12dbf7d3727 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/client.rpt @@ -52,7 +52,7 @@ connected write '{"name":"list_subjects","arguments":{}}' -read '{"structuredContent":["orders-value","payments-value"],"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' +read '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' read closed write close diff --git a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt index ff9dd790187..975de2d6c32 100644 --- a/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt +++ b/specs/binding-mcp-schema-registry.spec/src/main/scripts/io/aklivity/zilla/specs/binding/mcp/schema/registry/streams/mcp/list.subjects/server.rpt @@ -54,7 +54,7 @@ read '{"name":"list_subjects","arguments":{}}' write flush -write '{"structuredContent":["orders-value","payments-value"],"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' +write '{"structuredContent":{"result":["orders-value","payments-value"]},"content":[{"type":"text","text":"Subjects: [\\"orders-value\\",\\"payments-value\\"]"}],"isError":false}' write flush write close