Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion specification/draft/apps.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2170,7 +2170,7 @@ App-provided tools and [WebMCP](https://webmachinelearning.github.io/webmcp/) (W

**SDK mirroring (Apps → WebMCP).** The SDK SHOULD mirror tools registered with `registerTool()` into `document.modelContext` when the document provides one, so a WebMCP-enabled Host (one that consumes `document.modelContext`, natively or through its own machinery) discovers them without speaking the App `tools/list` channel. The `tools/list` / `tools/call` wire path is unaffected: mirroring is additive. For native registration to succeed inside a sandboxed iframe, the embedder MUST delegate the `tools` [Permissions Policy feature](https://webmachinelearning.github.io/webmcp/#permissions-policy) to the frame chain (e.g. `allow="tools"` on the iframe); where it is not delegated, registration fails and the tool remains available over `tools/list` only.

**SDK bridging (WebMCP → Apps).** The SDK SHOULD serve tools the page registered on `document.modelContext` over the App `tools/list` / `tools/call` channel, declaring the `tools` app capability on the View's behalf when the page carries WebMCP tools at connect time, and relaying `toolchange` as `notifications/tools/list_changed`, so WebMCP-authored Views work on Hosts that do not provide WebMCP. A View that must also run where neither the browser nor the Host provides `document.modelContext` can install the SDK's polyfill (`installWebMcpPolyfill()`) before registering tools; a Host MAY instead provide `document.modelContext` itself.
**SDK bridging (WebMCP → Apps).** The SDK SHOULD serve tools the page registered on `document.modelContext` over the App `tools/list` / `tools/call` channel, declaring the `tools` app capability on the View's behalf when the page carries WebMCP tools at connect time, and relaying `toolchange` as `notifications/tools/list_changed`, so WebMCP-authored Views work on Hosts that do not provide WebMCP. A View that must also run where neither the browser nor the Host provides `document.modelContext` can install a polyfill of its own before registering tools; a Host MAY instead provide `document.modelContext` itself.

**No double exposure.**
- The SDK MUST NOT echo a tool back onto the surface it came from: a tool mirrored into `document.modelContext` is not re-served from there over `tools/list` (tools registered with `registerTool()` shadow same-named WebMCP entries), and a bridged WebMCP tool is not re-mirrored into `document.modelContext`.
Expand Down
3 changes: 2 additions & 1 deletion src/app-webmcp-interop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
} from "@modelcontextprotocol/server";
import { z } from "zod/v4";

import { App, installWebMcpPolyfill } from "./app.js";
import { App } from "./app.js";
import { installWebMcpPolyfill } from "./webmcp-polyfill.js";
import {
LATEST_PROTOCOL_VERSION,
McpUiInitializeRequestSchema,
Expand Down
1 change: 0 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export type {
LegacyRequestHandlerSetter,
} from "./legacy-handlers.js";
export { EventDispatcher } from "./events.js";
export { installWebMcpPolyfill } from "./webmcp-polyfill.js";

import { PostMessageTransport } from "./message-transport.js";
import {
Expand Down
17 changes: 9 additions & 8 deletions src/webmcp-polyfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
* for tools registered by this page to be read back and executed. No
* cross-frame support: `exposedTo` is accepted and ignored.
*
* A WebMCP-authored View calls this before registering tools, so it also runs
* where neither the browser nor the Host provides `document.modelContext`;
* the {@link App `App`} then serves the registered tools to MCP Apps hosts
* over `tools/list` / `tools/call`. A native `document.modelContext` (or a
* polyfill the Host injected) wins: installing over one is a no-op, except
* that this polyfill replaces an earlier instance of itself, since
* `document.open()` keeps expando properties of a document that is written
* again.
* Not part of the public API: it backs this SDK's own tests of the
* WebMCP → Apps bridge. A WebMCP-authored View that must run where neither
* the browser nor the Host provides `document.modelContext` brings its own
* polyfill. A native `document.modelContext` (or a polyfill the Host
* injected) wins: installing over one is a no-op, except that this polyfill
* replaces an earlier instance of itself, since `document.open()` keeps
* expando properties of a document that is written again.
*/

import { WEBMCP_TOOL_NAME } from "./webmcp.js";
Expand All @@ -36,6 +35,8 @@ interface PolyfillTool {
* Install the WebMCP polyfill on `doc` (the current document by default).
* A no-op where `document.modelContext` already exists, unless that is an
* earlier instance of this polyfill.
*
* @internal
*/
export function installWebMcpPolyfill(
doc: Document = (globalThis as unknown as { document: Document }).document,
Expand Down
2 changes: 1 addition & 1 deletion src/webmcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function readModelContext(): unknown {
/**
* The document's WebMCP `modelContext` producer API, if the browser provides
* one natively, the host injected a polyfill into the View's HTML, or the
* page installed {@link installWebMcpPolyfill `installWebMcpPolyfill`}.
* page installed a polyfill of its own.
*
* @internal
*/
Expand Down
Loading