Skip to content

feat(binding-mcp-openapi): implement mcp_openapi proxy binding#1891

Open
jfallows wants to merge 7 commits into
developfrom
claude/elegant-maxwell-0otr88
Open

feat(binding-mcp-openapi): implement mcp_openapi proxy binding#1891
jfallows wants to merge 7 commits into
developfrom
claude/elegant-maxwell-0otr88

Conversation

@jfallows

Copy link
Copy Markdown
Contributor

Fixes #1673

Summary

Implements the mcp_openapi · proxy binding: a composite, config-generation-only binding that reads OpenAPI specs from catalogs and compiles each routed operation into mcp_http (#1675) configuration. It adds zero protocol hot-path code — all tools/* and resources/* behavior and response projection are provided by the generated mcp_http. This lets an MCP agent invoke any OpenAPI-described REST API as MCP tools/resources, configured entirely in zilla.yaml, with a single spec parse.

Accepts mcp, produces http. Modeled on the existing binding-openapi (client) composite.

How it works

  • Single composite namespace. At config-attach the generator parses each referenced spec once and emits one child namespace (registered via EngineContext.attachComposite) holding an inline catalog0 + one mcp_http0 binding whose routes exit the configured HTTP client. Because the exit's :authority/:scheme come from each spec's servers, all routed operations across all specs aggregate into that one namespace.
  • Thin 1:1 bridge. McpOpenapiProxyFactory relays mcp frames straight into the generated mcp_http0 — no per-operation dispatch, no new .idl.
  • Compilation mapping (per routed tool/resource): flattened <tool>-input schema (path/query params ∪ requestBody properties, _body suffix on collisions, union of required), <tool>-body projection, <tool>-output (full success-response schema, overridable via options.tools.<tool>.schemas.output), and route with.headers (:method/:scheme/:authority/:path with ${args.…} for tools and ${params.…} for resources, query params appended to :path).

Configurable exit

The generated mcp_http route exit is the engine property zilla.binding.mcp.openapi.http.client.exit (default sys:http_client), so deployments can target the global system HTTP client and ITs can redirect it to a test endpoint.

Reuse

Reuses binding-openapi's OpenapiParser and Openapi*View model for spec parsing rather than re-implementing it — the same way binding-openapi-asyncapi already consumes those views (classpath compilation + the existing opens; no new exports). Follow-ups #1889 / #1890 propose hoisting the OpenAPI/AsyncAPI model/view/parser APIs into dedicated runtime/common-* modules to formalize this reuse.

Testing

Test-first, mirroring binding-mcp-http. All green; full reactor clean install passes (only the Docker-image packaging step is skipped — it requires a Docker daemon unavailable in CI-less local runs).

  • Spec moduleSchemaTest (valid + invalid configs) and peer .rpt ITs (McpServerIT, HttpClientIT).
  • Runtime live-engine ITs (McpOpenapiProxyIT, driving the generated composite through a real engine): tool call, resource read (${params.…} capture), tools/list + resources/list discovery, flow control (~10 KB fragmented body), abort propagation, and upstream-error (isError) passthrough.
  • Generator unit tests: input-schema flattening + _body collision, deterministic property order, :scheme/:authority/:path derivation, schemas.output override, query-parameter interpolation, and multi-spec aggregation.
  • Adapter/config unit tests: options round-trip and configuration constants.

Notes

  • New modules runtime/binding-mcp-openapi and specs/binding-mcp-openapi.spec, registered in the runtime/specs/root poms.
  • No change to binding-openapi behavior; the only edit there was reverting a stray internal.view export so view reuse matches the binding-openapi-asyncapi precedent.

https://claude.ai/code/session_013Li1mTsigtRqijhbtm4Xp5


Generated by Claude Code

claude added 7 commits June 15, 2026 22:33
Add the mcp_openapi proxy binding, a composite config-generation-only
binding that compiles OpenAPI specs read from catalogs into mcp_http
binding configuration. For each routed operation it generates the
mcp_http options.tools/resources, input/body/output schemas, and route
with clauses (:method/:scheme/:authority/:path with ${args.}/${params.}
interpolation), wiring a single generated namespace whose mcp_http0
routes exit sys:http_client.

The binding adds no protocol hot-path code: a 1:1 mcp->mcp relay forwards
frames into the generated mcp_http0 binding, which provides all runtime
behavior. Reuses binding-openapi's OpenapiParser and views for spec
parsing (exporting its internal.view package).

Closes #1673
…e IT

Make the generated mcp_http route exit a configuration property
(zilla.binding.mcp.openapi.http.client.exit, default sys:http_client)
threaded through the factory into the composite generator, so an IT can
redirect it to a k3po-backed binding. Add McpOpenapiProxyIT exercising the
full pipeline end-to-end through a live engine: mcp tools/call -> generated
mcp_http -> external http0, asserting the derived HTTP request and the
projected tool result.

Align the create.pr mcp scripts with mcp_http's actual no-summary output
(content text is the compact projected JSON).

Revert the exports of binding-openapi internal.view added earlier; reuse
those views the same way binding-openapi-asyncapi does (classpath compile
with the existing opens), no export required.
Add live-engine and peer ITs for resources/read (${params.x} capture from
the resource uri template) and for tools/list and resources/list answered
from the generated mcp_http config, covering the resource and discovery
paths end-to-end.

Make the generated tool input schema deterministic by emitting requestBody
properties in sorted order (the OpenAPI view's property map is unordered),
so tools/list output is stable across runs.
Add create.pr.10k (fragmented request body exercising DATA/WINDOW relay
through the bridge), create.pr.aborted (ABORT propagation), and
create.pr.error (upstream 422 surfaced as an isError tool result) across
the mcp and http streams, with peer ITs and live-engine ITs, to meet the
required protocol coverage (flow control, abortive close, protocol error).
Add generator unit tests for the remaining wired paths: tool schemas.output
override, query-parameter interpolation into the generated :path, and
aggregation of multiple specs into a single composite namespace with per-tool
input subjects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

binding-mcp-openapi: implement mcp_openapi · proxy binding

2 participants