Skip to content

Bridge App-registered tools and WebMCP in both directions (middle ground for #797) - #826

Closed
ochafik wants to merge 1 commit into
modelcontextprotocol:mainfrom
ochafik:webmcp-interop
Closed

ochafik wants to merge 1 commit into
modelcontextprotocol:mainfrom
ochafik:webmcp-interop

Conversation

@ochafik

@ochafik ochafik commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Middle-ground counterpart to #798, along the lines of Option 3 in #797 and of #796: instead of deprecating App.registerTool in favor of WebMCP, keep both tool surfaces first-class and have the SDK bridge them in both directions.

  • Apps → WebMCP: tools registered with App.registerTool() are mirrored into document.modelContext when the document provides one, so a WebMCP-enabled host (one that consumes document.modelContext, e.g. natively) sees them without speaking the Apps tools/list channel. The wire path is untouched; mirroring is additive.
  • WebMCP → Apps: tools the page registers on document.modelContext are served over the existing tools/list / tools/call iframe channel — the SDK declares the tools capability on the View's behalf when the page carries WebMCP tools at connect time, and relays toolchange as notifications/tools/list_changed. installWebMcpPolyfill() is the explore: add a thin bridge between WebMCP and MCP Apps Tools #796 "drop-in" for WebMCP-authored views that must also run where neither the browser nor the host provides document.modelContext.

No new wire messages, no host-side API changes, no deprecations: hosts keep bridge.listTools() / callTool(), views keep registerTool(), and a WebMCP-only host like the ones #797 anticipates reads document.modelContext as it would on any page. Dedup rules: registered tools shadow same-named WebMCP entries (which also keeps a tool's own mirror from being echoed back), only the frame's own tools are served, and the spec text says a host able to discover a view's tools both ways consumes exactly one surface per view. Both behaviors are per-App opt-outs (mirrorToolsToWebMcp / bridgeWebMcpTools).

Spec: the informative "Relation to WebMCP" section becomes a normative "WebMCP Interoperability" section with the WebMCP ↔ MCP mapping table (same shapes as #798: results as {content: [], structuredContent}, non-object values wrapped as {result: v}, thrown errors as isError: true).

Like #798, this is meant to make the option concrete for discussion, not to merge as-is — but unlike #798 it is non-breaking, so it would not need a major release.

Test plan

bun test src passes (253, including 8 new interop tests driving a real Server ↔ App pair over InMemoryTransport with the SDK polyfill as the model context); tsc --noEmit and typedoc --treatValidationWarningsAsErrors are clean. Not verified: native WebMCP (Chrome), real third-party hosts, the browser e2e suite.

Known limits / open questions

  • Mirror re-registration on tool.update() aborts-then-registers; an implementation whose signal-triggered unregistration is asynchronous could reject the re-registration as a duplicate (works with the SDK polyfill; same pattern as Prototype WebMCP for View-exposed tools, to illustrate #797 #798).
  • A model context that appears only after connect() is picked up for mirroring at the next reconnect or tool mutation, but the tools capability cannot be declared retroactively — WebMCP tools registered late on a page that had none at connect time are not served (capability is fixed at ui/initialize).
  • toolchange fired by the App's own mirror registrations currently produces an extra notifications/tools/list_changed beside the one registerTool()/enable()/… already send.
  • Native registration inside the sandbox needs the tools Permissions Policy delegated (feat: add support for 'tools' permission #691); where it is not, mirroring fails gracefully and tools/list remains the fallback.

cc @liady @idosal


Generated by Claude Code

Keep App.registerTool and the tools/list / tools/call channel as they are,
and make the two tool surfaces interoperable:

- Tools registered with App.registerTool are mirrored into
  document.modelContext when the document provides one, so WebMCP-enabled
  hosts discover them without speaking the Apps tools channel.
- Tools the page registers on document.modelContext are served over
  tools/list / tools/call (declaring the tools capability when the page
  carries WebMCP tools at connect time), with toolchange relayed as
  notifications/tools/list_changed, so WebMCP-authored views work on
  MCP Apps hosts.
- installWebMcpPolyfill() gives WebMCP-authored views a drop-in
  document.modelContext where neither the browser nor the host provides
  one.

Registered tools shadow same-named WebMCP entries, which also keeps a
tool's own mirror from being listed twice; only the frame's own tools are
served. Both behaviors can be switched off per App
(mirrorToolsToWebMcp / bridgeWebMcpTools).
@pkg-pr-new

pkg-pr-new Bot commented Oct 26, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/@modelcontextprotocol/ext-apps@826

@modelcontextprotocol/server-basic-preact

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-preact@826

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-react@826

@modelcontextprotocol/server-basic-solid

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-solid@826

@modelcontextprotocol/server-basic-svelte

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-svelte@826

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vanillajs@826

@modelcontextprotocol/server-basic-vue

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vue@826

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/@modelcontextprotocol/server-budget-allocator@826

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/@modelcontextprotocol/server-cohort-heatmap@826

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/@modelcontextprotocol/server-customer-segmentation@826

@modelcontextprotocol/server-debug

npm i https://pkg.pr.new/@modelcontextprotocol/server-debug@826

@modelcontextprotocol/server-lazy-auth

npm i https://pkg.pr.new/@modelcontextprotocol/server-lazy-auth@826

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/@modelcontextprotocol/server-map@826

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/@modelcontextprotocol/server-pdf@826

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/@modelcontextprotocol/server-scenario-modeler@826

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/@modelcontextprotocol/server-shadertoy@826

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/@modelcontextprotocol/server-sheet-music@826

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/@modelcontextprotocol/server-system-monitor@826

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/@modelcontextprotocol/server-threejs@826

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/@modelcontextprotocol/server-transcript@826

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/@modelcontextprotocol/server-video-resource@826

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/@modelcontextprotocol/server-wiki-explorer@826

commit: 6044135

@ochafik

ochafik commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

superseded by #829

@ochafik ochafik closed this Oct 27, 2026
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.

1 participant