From fa1274490873f869c9a084e1abb9cf3031d288c7 Mon Sep 17 00:00:00 2001 From: Aqil Aziz Date: Wed, 17 Jun 2026 22:51:57 +0700 Subject: [PATCH 01/45] docs: fix map-server readme and code of conduct links (#657) --- CONTRIBUTING.md | 2 +- examples/map-server/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6592106..e28dcd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -502,7 +502,7 @@ Alternatively, use the [workflow dispatch](https://github.com/modelcontextprotoc ## Code of Conduct -This project follows our [Code of Conduct](CODE_OF_CONDUCT.md). Please review it before contributing. +This project follows our [Code of Conduct](https://github.com/modelcontextprotocol/.github/blob/main/CODE_OF_CONDUCT.md). Please review it before contributing. ## Reporting Issues diff --git a/examples/map-server/README.md b/examples/map-server/README.md index 4c4de5f..e1e044a 100644 --- a/examples/map-server/README.md +++ b/examples/map-server/README.md @@ -122,7 +122,7 @@ Vanilla TypeScript app that: - [`server.ts`](server.ts) - MCP server with geocode and show-map tools - [`mcp-app.html`](mcp-app.html) / [`src/mcp-app.ts`](src/mcp-app.ts) - CesiumJS globe UI -- [`server-utils.ts`](server-utils.ts) - HTTP server utilities +- [`main.ts`](main.ts) - HTTP and stdio server utilities ## Notes From ea8775dcb5534cc8977c5137fee5572b0cd94f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20G=C3=B6k?= <116498667+ensargx@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:43:37 +0300 Subject: [PATCH 02/45] docs: API Docs link for ext-apps/server (#697) Updated API documentation link for ext-apps/server. The link `/server.html` does not exists. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7348e15..d0677ef 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ UI metadata. | `@modelcontextprotocol/ext-apps` | Build interactive Views (App class, PostMessageTransport) | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/app.html) | | `@modelcontextprotocol/ext-apps/react` | React hooks for Views (useApp, useHostStyles, etc.) | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/_modelcontextprotocol_ext-apps_react.html) | | `@modelcontextprotocol/ext-apps/app-bridge` | Embed and communicate with Views in your chat client | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/app-bridge.html) | -| `@modelcontextprotocol/ext-apps/server` | Register tools and resources on your MCP server | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/server.html) | +| `@modelcontextprotocol/ext-apps/server` | Register tools and resources on your MCP server | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/server-helpers.html) | There's no _supported_ host implementation in this repo (beyond the [examples/basic-host](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) From f0d9582eaa63faeec754c8b026ec4a9b3f626cfe Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Tue, 7 Jul 2026 14:02:07 -0400 Subject: [PATCH 03/45] spec: add missing HostCapabilities fields (#653) --- specification/draft/apps.mdx | 39 ++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 1a14d3f..96c8286 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -659,6 +659,22 @@ Example: They describe the features and capabilities that the Host supports. ```typescript +/** Content block modalities that the host supports for a given capability. */ +interface SupportedContentBlockModalities { + /** Host supports text content blocks. */ + text?: {}; + /** Host supports image content blocks. */ + image?: {}; + /** Host supports audio content blocks. */ + audio?: {}; + /** Host supports resource content blocks. */ + resource?: {}; + /** Host supports resource link content blocks. */ + resourceLink?: {}; + /** Host supports structured content. */ + structuredContent?: {}; +} + interface HostCapabilities { /** Experimental features (structure TBD). */ experimental?: {}; @@ -678,14 +694,6 @@ interface HostCapabilities { }; /** Host accepts log messages. */ logging?: {}; - /** - * Host supports LLM sampling (sampling/createMessage) from the view. - * Mirrors MCP ClientCapabilities.sampling so hosts can pass it through. - */ - sampling?: { - /** Host supports tool use via `tools` and `toolChoice` params (SEP-1577). */ - tools?: {}; - }; /** Sandbox configuration applied by the host. */ sandbox?: { /** Permissions granted by the host (camera, microphone, geolocation, clipboard-write). */ @@ -707,6 +715,21 @@ interface HostCapabilities { baseUriDomains?: string[]; }; }; + /** + * Host accepts context updates (ui/update-model-context) to be included in + * the model's context for future turns. + */ + updateModelContext?: SupportedContentBlockModalities; + /** Host supports receiving content messages (ui/message) from the view. */ + message?: SupportedContentBlockModalities; + /** + * Host supports LLM sampling (sampling/createMessage) from the view. + * Mirrors MCP ClientCapabilities.sampling so hosts can pass it through. + */ + sampling?: { + /** Host supports tool use via `tools` and `toolChoice` params (SEP-1577). */ + tools?: {}; + }; } ``` From cd969dbc312923a0a407ed76f8679666be45e801 Mon Sep 17 00:00:00 2001 From: Andrew Khadder <54488379+khandrew1@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:05:17 -0700 Subject: [PATCH 04/45] docs: add mcp-use inspector badge to supported clients (#650) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d0677ef..506b255 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ it will guide you through the rest. Goose Postman MCPJam + mcp-use

> [!NOTE] From cf87f2a2c2581b2bc45ff4848aac9fa7e106a576 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 21:29:16 +0300 Subject: [PATCH 05/45] ci: fix README prettier build failure (#698) * Initial plan * fix: apply Prettier formatting to README table row --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 506b255..2fd89ff 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ UI metadata. | `@modelcontextprotocol/ext-apps` | Build interactive Views (App class, PostMessageTransport) | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/app.html) | | `@modelcontextprotocol/ext-apps/react` | React hooks for Views (useApp, useHostStyles, etc.) | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/_modelcontextprotocol_ext-apps_react.html) | | `@modelcontextprotocol/ext-apps/app-bridge` | Embed and communicate with Views in your chat client | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/app-bridge.html) | -| `@modelcontextprotocol/ext-apps/server` | Register tools and resources on your MCP server | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/server-helpers.html) | +| `@modelcontextprotocol/ext-apps/server` | Register tools and resources on your MCP server | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/server-helpers.html) | There's no _supported_ host implementation in this repo (beyond the [examples/basic-host](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) From b383847bf2c7f4f5fa37e3bd054205f71618e035 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 13:35:51 -0700 Subject: [PATCH 06/45] chore: prepare SDK v2 dependencies --- examples/basic-server-preact/package.json | 2 + examples/basic-server-react/package.json | 2 + examples/basic-server-solid/package.json | 2 + examples/basic-server-svelte/package.json | 2 + examples/basic-server-vanillajs/package.json | 2 + examples/basic-server-vue/package.json | 2 + examples/budget-allocator-server/package.json | 2 + examples/cohort-heatmap-server/package.json | 2 + .../customer-segmentation-server/package.json | 2 + examples/debug-server/package.json | 2 + examples/integration-server/package.json | 2 + examples/lazy-auth-server/package.json | 1 + examples/map-server/package.json | 2 + examples/pdf-server/package.json | 2 + examples/quickstart/package.json | 2 + examples/scenario-modeler-server/package.json | 2 + examples/shadertoy-server/package.json | 2 + examples/sheet-music-server/package.json | 2 + examples/system-monitor-server/package.json | 2 + examples/threejs-server/package.json | 2 + examples/transcript-server/package.json | 2 + examples/video-resource-server/package.json | 2 + examples/wiki-explorer-server/package.json | 2 + package-lock.json | 537 +++++------------- package.json | 10 +- 25 files changed, 187 insertions(+), 405 deletions(-) diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 5127202..1a63232 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index e4d1233..821fc96 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -35,6 +35,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index 8f75ac8..997bc60 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index f877bf7..e83772f 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 5d47f2c..64e1758 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index d27ddc9..e4cd0b4 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 19a1d25..8af1a6c 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index 8d1268b..99e9d9f 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index 4511de9..59e3eb1 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index 3417a8f..02e9fbf 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -35,6 +35,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.1.13" }, diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json index f6715a4..3f9d8bb 100644 --- a/examples/integration-server/package.json +++ b/examples/integration-server/package.json @@ -16,6 +16,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index f3ed6cd..086f20a 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/map-server/package.json b/examples/map-server/package.json index 5dc23fe..b0c629a 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index 4d960f1..aeb52c7 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -27,6 +27,8 @@ "dependencies": { "@cantoo/pdf-lib": "^2.6.5", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index 99948cc..1003637 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -16,6 +16,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0" diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index aa1e668..a7e8a9a 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index ccf15a4..0c27501 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index ca9fa90..6cf175a 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "abcjs": "^6.4.4", "cors": "^2.8.5", diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index 12e8cea..760d07f 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index cfb5686..9dda288 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index dcba3d7..ce5b737 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index b0d04af..605b8de 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -25,6 +25,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index fe511ad..62f1de8 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -27,6 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cheerio": "^1.0.0", "cors": "^2.8.5", diff --git a/package-lock.json b/package-lock.json index 29e9058..5514bf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,10 @@ }, "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -44,16 +47,19 @@ "typedoc": "^0.28.14", "typedoc-github-theme": "^0.4.0", "typescript": "^5.9.3", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "engines": { "node": ">=20" }, "peerDependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^3.25.0 || ^4.0.0" + "zod": "^4.2.0" }, "peerDependenciesMeta": { "react": { @@ -89,29 +95,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-host/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-host/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-preact": { "name": "@modelcontextprotocol/server-basic-preact", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -133,29 +124,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-preact/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-preact/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-react": { "name": "@modelcontextprotocol/server-basic-react", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -180,29 +156,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-react/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-react/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-solid": { "name": "@modelcontextprotocol/server-basic-solid", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -224,29 +185,14 @@ "vite-plugin-solid": "^2.11.12" } }, - "examples/basic-server-solid/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-solid/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-svelte": { "name": "@modelcontextprotocol/server-basic-svelte", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -268,29 +214,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-svelte/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-svelte/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-vanillajs": { "name": "@modelcontextprotocol/server-basic-vanillajs", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -310,29 +241,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-vanillajs/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-vanillajs/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-vue": { "name": "@modelcontextprotocol/server-basic-vue", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -354,29 +270,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-vue/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-vue/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/budget-allocator-server": { "name": "@modelcontextprotocol/server-budget-allocator", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", @@ -397,29 +298,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/budget-allocator-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/budget-allocator-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/cohort-heatmap-server": { "name": "@modelcontextprotocol/server-cohort-heatmap", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -444,29 +330,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/cohort-heatmap-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/cohort-heatmap-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/customer-segmentation-server": { "name": "@modelcontextprotocol/server-customer-segmentation", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", @@ -487,29 +358,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/customer-segmentation-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/customer-segmentation-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/debug-server": { "name": "@modelcontextprotocol/server-debug", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.1.13" }, @@ -529,27 +385,12 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/debug-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/debug-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/integration-server": { "version": "1.7.4", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -573,29 +414,13 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/integration-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/integration-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/lazy-auth-server": { "name": "@modelcontextprotocol/server-lazy-auth", "version": "1.7.4", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -615,29 +440,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/lazy-auth-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/lazy-auth-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/map-server": { "name": "@modelcontextprotocol/server-map", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -657,30 +467,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/map-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/map-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/pdf-server": { "name": "@modelcontextprotocol/server-pdf", "version": "1.7.4", "license": "MIT", "dependencies": { "@cantoo/pdf-lib": "^2.6.5", + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -701,23 +496,6 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/pdf-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/pdf-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", "version": "1.7.4", @@ -731,7 +509,9 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0" @@ -748,16 +528,6 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/quickstart/node_modules/@types/node": { - "version": "22.19.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.5.tgz", - "integrity": "sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", "version": "1.7.4", @@ -772,7 +542,9 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", @@ -798,29 +570,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/scenario-modeler-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/scenario-modeler-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/shadertoy-server": { "name": "@modelcontextprotocol/server-shadertoy", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -840,29 +597,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/shadertoy-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/shadertoy-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/sheet-music-server": { "name": "@modelcontextprotocol/server-sheet-music", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "abcjs": "^6.4.4", "cors": "^2.8.5", @@ -883,29 +625,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/sheet-music-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/sheet-music-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/system-monitor-server": { "name": "@modelcontextprotocol/server-system-monitor", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "chart.js": "^4.4.0", "cors": "^2.8.5", @@ -927,29 +654,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/system-monitor-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/system-monitor-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/threejs-server": { "name": "@modelcontextprotocol/server-threejs", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -976,29 +688,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/threejs-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/threejs-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/transcript-server": { "name": "@modelcontextprotocol/server-transcript", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -1019,29 +716,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/transcript-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/transcript-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/video-resource-server": { "name": "@modelcontextprotocol/server-video-resource", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -1061,29 +743,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/video-resource-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/video-resource-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/wiki-explorer-server": { "name": "@modelcontextprotocol/server-wiki-explorer", "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/node": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", "cheerio": "^1.0.0", "cors": "^2.8.5", @@ -1105,23 +772,6 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/wiki-explorer-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/wiki-explorer-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/code-frame": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", @@ -2576,6 +2226,53 @@ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", + "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.4.tgz", + "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/express": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/express/-/express-2.0.0-beta.4.tgz", + "integrity": "sha512-7hI8bEomz6rMn8sKYRVIZgQJsrWIjIS9WiPyIp3X2pMyLSCC7hB5km0kg+9SygWygTIRnPfte0tXevRB3mXEHQ==", + "license": "MIT", + "dependencies": { + "cors": "^2.8.5" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@modelcontextprotocol/server": "^2.0.0-beta.4", + "express": "^4.18.0 || ^5.0.0" + } + }, "node_modules/@modelcontextprotocol/ext-apps": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.1.tgz", @@ -2609,6 +2306,27 @@ "resolved": "examples/basic-host", "link": true }, + "node_modules/@modelcontextprotocol/node": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/node/-/node-2.0.0-beta.4.tgz", + "integrity": "sha512-OqbrRVNYNfLxBB2BRkP5oIHKHa0IPHluMbaLgw7YCRKcj5be9ExzyWFJY3KVMb+1F2AVn+0SRoH0XKcNzh3HYQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@modelcontextprotocol/server": "^2.0.0-beta.4", + "hono": "^4.11.4" + }, + "peerDependenciesMeta": { + "hono": { + "optional": true + } + } + }, "node_modules/@modelcontextprotocol/quickstart": { "resolved": "examples/quickstart", "link": true @@ -2653,6 +2371,19 @@ } } }, + "node_modules/@modelcontextprotocol/server": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.4.tgz", + "integrity": "sha512-pjMZcNEt1dOq0aJCcY3b7w1Ayh+qmQN2xlfTELcGV9yiAjEGIczBPBLWWW0k0zzo5T8kiv15jtKPsWeHGxLvLg==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0-beta.4", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@modelcontextprotocol/server-basic-preact": { "resolved": "examples/basic-server-preact", "link": true diff --git a/package.json b/package.json index 8c6ead5..35bcdf0 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,10 @@ "author": "Olivier Chafik", "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -105,13 +108,16 @@ "typedoc": "^0.28.14", "typedoc-github-theme": "^0.4.0", "typescript": "^5.9.3", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "peerDependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/core": "2.0.0-beta.4", "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^3.25.0 || ^4.0.0" + "zod": "^4.2.0" }, "peerDependenciesMeta": { "react": { From 209cfc62413b7eea46c127ddd756ce58bd2da095 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 13:49:10 -0700 Subject: [PATCH 07/45] refactor: compose protocol-neutral event dispatch --- src/app-bridge.test.ts | 2 +- src/app-bridge.ts | 120 ++++++++++++++++--- src/app.ts | 139 +++++++++++++++++----- src/events.test.ts | 82 +++++++++++++ src/events.ts | 261 ++++++----------------------------------- typedoc.config.mjs | 2 +- 6 files changed, 335 insertions(+), 271 deletions(-) create mode 100644 src/events.test.ts diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index a4fe1de..7adbb16 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -2713,7 +2713,7 @@ describe("isToolVisibilityAppOnly", () => { expect(a).toEqual([]); }); - it("App.onEventDispatch merges hostcontext before listeners fire", async () => { + it("App merges hostcontext before listeners fire", async () => { let seen: unknown; app.addEventListener("hostcontextchanged", () => { seen = app.getHostContext(); diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 23383c4..18d7671 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -47,7 +47,10 @@ import { ProtocolOptions, RequestOptions, } from "@modelcontextprotocol/sdk/shared/protocol.js"; -import { ProtocolWithEvents } from "./events"; +import { EventDispatcher } from "./events"; +import type { ZodLiteral, ZodObject } from "zod/v4"; + +type MethodSchema = ZodObject<{ method: ZodLiteral }>; import { type AppNotification, @@ -303,16 +306,36 @@ export type AppBridgeEventMap = { * await bridge.connect(transport); * ``` */ -export class AppBridge extends ProtocolWithEvents< - AppRequest, - AppNotification, - AppResult, - AppBridgeEventMap -> { +export class AppBridge extends Protocol { private _appCapabilities?: McpUiAppCapabilities; private _hostContext: McpUiHostContext = {}; private _appInfo?: Implementation; private _initializedReceived = false; + private readonly _registeredMethods = new Set(); + private readonly _registeredEvents = new Set(); + private readonly _events = new EventDispatcher(); + + // ProtocolWithEvents previously owned these registration guards. Keep them + // on AppBridge to preserve the existing double-registration behavior while + // event fan-out moves to protocol-independent composition. Arrow-function + // fields let Protocol's constructor run before the guards initialize. + override setRequestHandler: Protocol< + AppRequest, + AppNotification, + AppResult + >["setRequestHandler"] = (schema, handler) => { + this._assertMethodNotRegistered(schema, "setRequestHandler"); + super.setRequestHandler(schema, handler); + }; + + override setNotificationHandler: Protocol< + AppRequest, + AppNotification, + AppResult + >["setNotificationHandler"] = (schema, handler) => { + this._assertMethodNotRegistered(schema, "setNotificationHandler"); + super.setNotificationHandler(schema, handler); + }; /** * Wrap every handler registered via `replaceRequestHandler` with a check @@ -324,13 +347,14 @@ export class AppBridge extends ProtocolWithEvents< * * @see {@link https://github.com/anthropics/claude-ai-mcp/issues/149 claude-ai-mcp#149} */ - private _baseReplaceRequestHandler = this.replaceRequestHandler; - protected override replaceRequestHandler: Protocol< + protected replaceRequestHandler: Protocol< AppRequest, AppNotification, AppResult >["setRequestHandler"] = (schema, handler) => { - this._baseReplaceRequestHandler(schema, (request, extra) => { + const method = (schema as MethodSchema).shape.method.value; + this._registeredMethods.add(method); + super.setRequestHandler(schema, (request, extra) => { if (!this._initializedReceived) { console.warn( `[ext-apps] AppBridge received '${request.method}' before ` + @@ -343,7 +367,9 @@ export class AppBridge extends ProtocolWithEvents< }); }; - protected readonly eventSchemas = { + private readonly eventSchemas: { + [K in keyof AppBridgeEventMap]: MethodSchema; + } = { sizechange: McpUiSizeChangedNotificationSchema, sandboxready: McpUiSandboxProxyReadyNotificationSchema, initialized: McpUiInitializedNotificationSchema, @@ -351,6 +377,74 @@ export class AppBridge extends ProtocolWithEvents< loggingmessage: LoggingMessageNotificationSchema, }; + protected setEventHandler( + event: K, + handler: ((params: AppBridgeEventMap[K]) => void) | undefined, + ): void { + this._ensureEventSlot(event); + this._events.setHandler(event, handler); + } + + protected getEventHandler( + event: K, + ): ((params: AppBridgeEventMap[K]) => void) | undefined { + return this._events.getHandler(event); + } + + addEventListener( + event: K, + handler: (params: AppBridgeEventMap[K]) => void, + ): void { + this._ensureEventSlot(event); + this._events.addEventListener(event, handler); + } + + removeEventListener( + event: K, + handler: (params: AppBridgeEventMap[K]) => void, + ): void { + this._events.removeEventListener(event, handler); + } + + private _ensureEventSlot( + event: K, + ): void { + if (this._registeredEvents.has(event)) return; + const schema = this.eventSchemas[event]; + if (!schema) throw new Error(`Unknown event: ${String(event)}`); + this.setNotificationHandler(schema, (notification) => { + const params = (notification as { params: AppBridgeEventMap[K] }).params; + if (event === "initialized") this._initializedReceived = true; + this._events.dispatch(event, params); + }); + this._registeredEvents.add(event); + } + + protected warnIfRequestHandlerReplaced( + name: string, + previous: unknown, + next: unknown, + ): void { + if (previous && next) { + console.warn( + `[MCP Apps] ${name} handler replaced. ` + + `Previous handler will no longer be called.`, + ); + } + } + + private _assertMethodNotRegistered(schema: unknown, via: string): void { + const method = (schema as MethodSchema).shape.method.value; + if (this._registeredMethods.has(method)) { + throw new Error( + `Handler for "${method}" already registered (via ${via}). ` + + `Use addEventListener() to attach multiple listeners, ` + + `or the on* setter for replace semantics.`, + ); + } + this._registeredMethods.add(method); + } + /** * Create a new AppBridge instance. * @@ -392,9 +486,7 @@ export class AppBridge extends ProtocolWithEvents< ) { super(options); - this.addEventListener("initialized", () => { - this._initializedReceived = true; - }); + this._ensureEventSlot("initialized"); this._hostContext = options?.hostContext || {}; diff --git a/src/app.ts b/src/app.ts index adfad5c..c586c31 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,7 @@ import { type RequestOptions, mergeCapabilities, + Protocol, ProtocolOptions, } from "@modelcontextprotocol/sdk/shared/protocol.js"; @@ -32,8 +33,8 @@ import { ToolListChangedNotification, } from "@modelcontextprotocol/sdk/types.js"; import { AppNotification, AppRequest, AppResult } from "./types"; -import { ProtocolWithEvents } from "./events"; -export { ProtocolWithEvents }; +import { EventDispatcher } from "./events"; +export { EventDispatcher } from "./events"; import { PostMessageTransport } from "./message-transport"; import { LATEST_PROTOCOL_VERSION, @@ -74,7 +75,9 @@ import { standardSchemaToJsonSchema, validateStandardSchema, } from "./standard-schema"; -import { z } from "zod/v4"; +import { z, type ZodLiteral, type ZodObject } from "zod/v4"; + +type MethodSchema = ZodObject<{ method: ZodLiteral }>; export type { StandardSchemaV1, @@ -271,9 +274,9 @@ export type RegisteredAppTool = { /** * Maps DOM-style event names to their notification `params` types. * - * Used by {@link App `App`} (which extends {@link ProtocolWithEvents `ProtocolWithEvents`}) - * to provide type-safe `addEventListener` / `removeEventListener` and - * singular `on*` handler support. + * Used by {@link App `App`} and its composed {@link EventDispatcher} to + * provide type-safe `addEventListener` / `removeEventListener` and singular + * `on*` handler support. */ export type AppEventMap = { toolinput: McpUiToolInputNotification["params"]; @@ -304,16 +307,14 @@ export type AppEventMap = { * 3. **Interactive**: Send requests, receive notifications, call tools * 4. **Teardown**: Host sends teardown request before unmounting * - * ## Inherited Methods + * ## Protocol and event methods * - * As a subclass of {@link ProtocolWithEvents `ProtocolWithEvents`}, `App` inherits: + * As a subclass of the MCP SDK's `Protocol`, `App` provides: * - `setRequestHandler()` - Register handlers for requests from host * - `setNotificationHandler()` - Register handlers for notifications from host * - `addEventListener()` - Append a listener for a notification event (multi-listener) * - `removeEventListener()` - Remove a previously added listener * - * @see {@link ProtocolWithEvents `ProtocolWithEvents`} for the DOM-model event system - * * ## Notification Setters (DOM-model `on*` handlers) * * For common notifications, the `App` class provides getter/setter properties @@ -342,17 +343,37 @@ export type AppEventMap = { * await app.connect(); * ``` */ -export class App extends ProtocolWithEvents< - AppRequest, - AppNotification, - AppResult, - AppEventMap -> { +export class App extends Protocol { private _hostCapabilities?: McpUiHostCapabilities; private _hostInfo?: Implementation; private _hostContext?: McpUiHostContext; private _registeredTools: { [name: string]: RegisteredAppTool } = {}; private _initializedSent = false; + private readonly _registeredMethods = new Set(); + private readonly _registeredEvents = new Set(); + private readonly _events = new EventDispatcher(); + + // ProtocolWithEvents previously owned these registration guards. Keep them + // on App to preserve the existing double-registration behavior while event + // fan-out moves to protocol-independent composition. Arrow-function fields + // ensure Protocol's constructor uses its own methods before the guards init. + override setRequestHandler: Protocol< + AppRequest, + AppNotification, + AppResult + >["setRequestHandler"] = (schema, handler) => { + this._assertMethodNotRegistered(schema, "setRequestHandler"); + super.setRequestHandler(schema, handler); + }; + + override setNotificationHandler: Protocol< + AppRequest, + AppNotification, + AppResult + >["setNotificationHandler"] = (schema, handler) => { + this._assertMethodNotRegistered(schema, "setNotificationHandler"); + super.setNotificationHandler(schema, handler); + }; /** * Warn if a host-bound method is called before {@link connect `connect`} has @@ -378,7 +399,9 @@ export class App extends ProtocolWithEvents< console.warn(`${msg}. This will throw in a future release.`); } - protected readonly eventSchemas = { + private readonly eventSchemas: { + [K in keyof AppEventMap]: MethodSchema; + } = { toolinput: McpUiToolInputNotificationSchema, toolinputpartial: McpUiToolInputPartialNotificationSchema, toolresult: McpUiToolResultNotificationSchema, @@ -427,31 +450,86 @@ export class App extends ProtocolWithEvents< console.warn(msg); } - protected override setEventHandler( + protected setEventHandler( event: K, handler: ((params: AppEventMap[K]) => void) | undefined, ): void { if (handler) this._assertHandlerTiming(event); - super.setEventHandler(event, handler); + this._ensureEventSlot(event); + this._events.setHandler(event, handler); } - override addEventListener( + addEventListener( event: K, handler: (params: AppEventMap[K]) => void, ): void { this._assertHandlerTiming(event); - super.addEventListener(event, handler); + this._ensureEventSlot(event); + this._events.addEventListener(event, handler); } - protected override onEventDispatch( + removeEventListener( event: K, - params: AppEventMap[K], + handler: (params: AppEventMap[K]) => void, ): void { - if (event === "hostcontextchanged") { - this._hostContext = { ...this._hostContext, ...params }; + this._events.removeEventListener(event, handler); + } + + private _ensureEventSlot(event: K): void { + if (this._registeredEvents.has(event)) return; + const schema = this.eventSchemas[event]; + if (!schema) throw new Error(`Unknown event: ${String(event)}`); + this.setNotificationHandler(schema, (notification) => { + const params = (notification as { params: AppEventMap[K] }).params; + if (event === "hostcontextchanged") { + this._hostContext = { ...this._hostContext, ...params }; + } + this._events.dispatch(event, params); + }); + this._registeredEvents.add(event); + } + + protected getEventHandler( + event: K, + ): ((params: AppEventMap[K]) => void) | undefined { + return this._events.getHandler(event); + } + + protected warnIfRequestHandlerReplaced( + name: string, + previous: unknown, + next: unknown, + ): void { + if (previous && next) { + console.warn( + `[MCP Apps] ${name} handler replaced. ` + + `Previous handler will no longer be called.`, + ); } } + protected replaceRequestHandler: Protocol< + AppRequest, + AppNotification, + AppResult + >["setRequestHandler"] = (schema, handler) => { + const method = (schema as MethodSchema).shape.method.value; + this._registeredMethods.add(method); + super.setRequestHandler(schema, handler); + }; + + private _assertMethodNotRegistered(schema: unknown, via: string): void { + const method = (schema as MethodSchema).shape.method.value; + if (this._registeredMethods.has(method)) { + throw new Error( + `Handler for "${method}" already registered (via ${via}). ` + + `Use addEventListener() to attach multiple listeners, ` + + `or the on* setter for replace semantics.`, + ); + } + this._registeredMethods.add(method); + } + /** * Create a new MCP App instance. * @@ -484,9 +562,8 @@ export class App extends ProtocolWithEvents< return {}; }); - // Eagerly register the hostcontextchanged event slot so that - // onEventDispatch (which merges into _hostContext) fires even if the - // user never assigns onhostcontextchanged or calls addEventListener. + // Eagerly register hostcontextchanged so the owner-side context merge runs + // even if the user never assigns a handler or adds a listener. this.setEventHandler("hostcontextchanged", undefined); } @@ -924,9 +1001,9 @@ export class App extends ProtocolWithEvents< * without replacing. * * Notification params are automatically merged into the internal host context - * via {@link onEventDispatch `onEventDispatch`} before any handler or listener - * fires. This means {@link getHostContext `getHostContext`} will return the - * updated values even before your callback runs. + * before any handler or listener fires. This means + * {@link getHostContext `getHostContext`} will return the updated values even + * before your callback runs. * * Register handlers before calling {@link connect `connect`} to avoid missing notifications. * diff --git a/src/events.test.ts b/src/events.test.ts new file mode 100644 index 0000000..7fd8708 --- /dev/null +++ b/src/events.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, it, spyOn } from "bun:test"; + +import { EventDispatcher } from "./events"; + +type TestEventMap = { + change: { value: number }; +}; + +describe("EventDispatcher", () => { + it("replaces the singular handler", () => { + const dispatcher = new EventDispatcher(); + const calls: string[] = []; + const first = () => calls.push("first"); + const second = () => calls.push("second"); + const warn = spyOn(console, "warn").mockImplementation(() => {}); + + try { + dispatcher.setHandler("change", first); + dispatcher.setHandler("change", second); + + expect(dispatcher.getHandler("change")).toBe(second); + dispatcher.dispatch("change", { value: 1 }); + expect(calls).toEqual(["second"]); + } finally { + warn.mockRestore(); + } + }); + + it("dispatches the singular handler before listeners in insertion order", () => { + const dispatcher = new EventDispatcher(); + const calls: string[] = []; + + dispatcher.setHandler("change", () => calls.push("handler")); + dispatcher.addEventListener("change", () => calls.push("listener-1")); + dispatcher.addEventListener("change", () => calls.push("listener-2")); + + dispatcher.dispatch("change", { value: 1 }); + + expect(calls).toEqual(["handler", "listener-1", "listener-2"]); + }); + + it("uses a listener snapshot when a listener is removed during dispatch", () => { + const dispatcher = new EventDispatcher(); + const calls: string[] = []; + const second = () => calls.push("second"); + const first = () => { + calls.push("first"); + dispatcher.removeEventListener("change", second); + }; + + dispatcher.addEventListener("change", first); + dispatcher.addEventListener("change", second); + + dispatcher.dispatch("change", { value: 1 }); + dispatcher.dispatch("change", { value: 2 }); + + expect(calls).toEqual(["first", "second", "first"]); + }); + + it("warns only when one singular handler replaces another", () => { + const dispatcher = new EventDispatcher(); + const warn = spyOn(console, "warn").mockImplementation(() => {}); + const first = () => {}; + const second = () => {}; + + try { + dispatcher.setHandler("change", first); + dispatcher.setHandler("change", undefined); + dispatcher.setHandler("change", first); + expect(warn).not.toHaveBeenCalled(); + + dispatcher.setHandler("change", second); + + expect(warn).toHaveBeenCalledTimes(1); + expect(String(warn.mock.calls[0]?.[0])).toContain( + "onchange handler replaced", + ); + } finally { + warn.mockRestore(); + } + }); +}); diff --git a/src/events.ts b/src/events.ts index 4d17ca4..b891867 100644 --- a/src/events.ts +++ b/src/events.ts @@ -1,17 +1,6 @@ -import { Protocol } from "@modelcontextprotocol/sdk/shared/protocol.js"; -import { - Request, - Notification, - Result, -} from "@modelcontextprotocol/sdk/types.js"; -import { ZodLiteral, ZodObject } from "zod/v4"; - -type MethodSchema = ZodObject<{ method: ZodLiteral }>; - /** - * Per-event state: a singular `on*` handler (replace semantics) plus a - * listener array (`addEventListener` semantics), mirroring the DOM model - * where `el.onclick` and `el.addEventListener("click", …)` coexist. + * Per-event state: a singular `on*` handler (replace semantics) plus an + * ordered listener array (`addEventListener` semantics). */ interface EventSlot { onHandler?: ((params: T) => void) | undefined; @@ -19,118 +8,19 @@ interface EventSlot { } /** - * Intermediate base class that adds DOM-style event support on top of the - * MCP SDK's `Protocol`. + * Protocol-independent DOM-style event fan-out. * - * The base `Protocol` class stores one handler per method: - * `setRequestHandler()` and `setNotificationHandler()` replace any existing - * handler for the same method silently. This class introduces a two-channel - * event model inspired by the DOM: + * Owners perform event-specific state updates before calling + * {@link dispatch}. Dispatch then invokes the singular handler followed by a + * snapshot of listeners in insertion order. * - * ### Singular `on*` handler (like `el.onclick`) - * - * Subclasses expose `get`/`set` pairs that delegate to - * {@link setEventHandler `setEventHandler`} / - * {@link getEventHandler `getEventHandler`}. Assigning replaces the previous - * handler; assigning `undefined` clears it. `addEventListener` listeners are - * unaffected. - * - * ### Multi-listener (`addEventListener` / `removeEventListener`) - * - * Append to a per-event listener array. Listeners fire in insertion order - * after the singular `on*` handler. - * - * ### Dispatch order - * - * When a notification arrives for a mapped event: - * 1. {@link onEventDispatch `onEventDispatch`} (subclass side-effects) - * 2. The singular `on*` handler (if set) - * 3. All `addEventListener` listeners in insertion order - * - * ### Double-set protection - * - * Direct calls to {@link setRequestHandler `setRequestHandler`} / - * {@link setNotificationHandler `setNotificationHandler`} throw if a handler - * for the same method has already been registered (through any path), so - * accidental overwrites surface as errors instead of silent bugs. - * - * @typeParam EventMap - Maps event names to the listener's `params` type. + * @typeParam EventMap - Maps event names to their listener parameter types. */ -export abstract class ProtocolWithEvents< - SendRequestT extends Request, - SendNotificationT extends Notification, - SendResultT extends Result, - EventMap extends Record, -> extends Protocol { - private _registeredMethods = new Set(); - private _eventSlots = new Map(); +export class EventDispatcher> { + private readonly _eventSlots = new Map(); - /** - * Event name → notification schema. Subclasses populate this so that - * the event system can lazily register a dispatcher with the correct - * schema on first use. - */ - protected abstract readonly eventSchemas: { - [K in keyof EventMap]: MethodSchema; - }; - - /** - * Called once per incoming notification, before any handlers or listeners - * fire. Subclasses may override to perform side effects such as merging - * notification params into cached state. - */ - protected onEventDispatch( - _event: K, - _params: EventMap[K], - ): void {} - - // ── Event system (DOM model) ──────────────────────────────────────── - - /** - * Lazily create the event slot and register a single dispatcher with the - * base `Protocol`. The dispatcher fans out to the `on*` handler and all - * `addEventListener` listeners. - */ - private _ensureEventSlot( - event: K, - ): EventSlot { - let slot = this._eventSlots.get(event) as - | EventSlot - | undefined; - if (!slot) { - const schema = this.eventSchemas[event]; - if (!schema) { - throw new Error(`Unknown event: ${String(event)}`); - } - slot = { listeners: [] }; - this._eventSlots.set(event, slot as EventSlot); - - // Claim this method so direct setNotificationHandler calls throw. - const method = schema.shape.method.value; - this._registeredMethods.add(method); - - const s = slot; // stable reference for the closure - super.setNotificationHandler(schema, (n) => { - const params = (n as { params: EventMap[K] }).params; - this.onEventDispatch(event, params); - // 1. Singular on* handler - s.onHandler?.(params); - // 2. addEventListener listeners — snapshot to tolerate removal during - // dispatch (e.g., a listener that calls removeEventListener on itself) - for (const l of [...s.listeners]) l(params); - }); - } - return slot; - } - - /** - * Set or clear the singular `on*` handler for an event. - * - * Replace semantics — like the DOM's `el.onclick = fn`. Assigning - * `undefined` clears the handler without affecting `addEventListener` - * listeners. - */ - protected setEventHandler( + /** Set, replace, or clear the singular handler for an event. */ + setHandler( event: K, handler: ((params: EventMap[K]) => void) | undefined, ): void { @@ -144,31 +34,16 @@ export abstract class ProtocolWithEvents< slot.onHandler = handler; } - /** - * Get the singular `on*` handler for an event, or `undefined` if none is - * set. `addEventListener` listeners are not reflected here. - */ - protected getEventHandler( + /** Get the singular handler for an event, if one is set. */ + getHandler( event: K, ): ((params: EventMap[K]) => void) | undefined { - return (this._eventSlots.get(event) as EventSlot | undefined) - ?.onHandler; + return (this._eventSlots.get(event) as + | EventSlot + | undefined)?.onHandler; } - /** - * Add a listener for a notification event. - * - * Unlike the singular `on*` handler, calling this multiple times appends - * listeners rather than replacing them. All registered listeners fire in - * insertion order after the `on*` handler when the notification arrives. - * - * Registration is lazy: the first call (for a given event, from either - * this method or the `on*` setter) registers a dispatcher with the base - * `Protocol`. - * - * @param event - Event name (a key of the `EventMap` type parameter). - * @param handler - Listener invoked with the notification `params`. - */ + /** Add a listener. Listeners run in insertion order after the handler. */ addEventListener( event: K, handler: (params: EventMap[K]) => void, @@ -176,11 +51,7 @@ export abstract class ProtocolWithEvents< this._ensureEventSlot(event).listeners.push(handler); } - /** - * Remove a previously registered event listener. The dispatcher stays - * registered even if the listener array becomes empty; future - * notifications simply have no listeners to call. - */ + /** Remove the first matching listener, if present. */ removeEventListener( event: K, handler: (params: EventMap[K]) => void, @@ -193,87 +64,29 @@ export abstract class ProtocolWithEvents< if (idx !== -1) slot.listeners.splice(idx, 1); } - // ── Handler registration with double-set protection ───────────────── - - // The two overrides below are arrow-function class fields rather than - // prototype methods so that Protocol's constructor — which registers its - // own ping/cancelled/progress handlers via `this.setRequestHandler` - // before our fields initialize — hits the base implementation and skips - // tracking. Converting these to proper methods would crash with - // `_registeredMethods` undefined during super(). - /** - * Registers a request handler. Throws if a handler for the same method - * has already been registered — use the `on*` setter (replace semantics) - * or `addEventListener` (multi-listener) for notification events. - * - * @throws {Error} if a handler for this method is already registered. + * Dispatch an event to its singular handler and a listener snapshot. + * Listener mutations during dispatch take effect on the next dispatch. */ - override setRequestHandler: Protocol< - SendRequestT, - SendNotificationT, - SendResultT - >["setRequestHandler"] = (schema, handler) => { - this._assertMethodNotRegistered(schema, "setRequestHandler"); - super.setRequestHandler(schema, handler); - }; - - /** - * Registers a notification handler. Throws if a handler for the same - * method has already been registered — use the `on*` setter (replace - * semantics) or `addEventListener` (multi-listener) for mapped events. - * - * @throws {Error} if a handler for this method is already registered. - */ - override setNotificationHandler: Protocol< - SendRequestT, - SendNotificationT, - SendResultT - >["setNotificationHandler"] = (schema, handler) => { - this._assertMethodNotRegistered(schema, "setNotificationHandler"); - super.setNotificationHandler(schema, handler); - }; - - /** - * Warn if a request handler `on*` setter is replacing a previously-set - * handler. Call from each request setter before updating the backing field. - */ - protected warnIfRequestHandlerReplaced( - name: string, - previous: unknown, - next: unknown, - ): void { - if (previous && next) { - console.warn( - `[MCP Apps] ${name} handler replaced. ` + - `Previous handler will no longer be called.`, - ); - } + dispatch(event: K, params: EventMap[K]): void { + const slot = this._eventSlots.get(event) as + | EventSlot + | undefined; + if (!slot) return; + slot.onHandler?.(params); + for (const l of [...slot.listeners]) l(params); } - /** - * Replace a request handler, bypassing double-set protection. Used by - * `on*` request-handler setters that need replace semantics. - */ - protected replaceRequestHandler: Protocol< - SendRequestT, - SendNotificationT, - SendResultT - >["setRequestHandler"] = (schema, handler) => { - const method = (schema as MethodSchema).shape.method.value; - this._registeredMethods.add(method); - super.setRequestHandler(schema, handler); - }; - - private _assertMethodNotRegistered(schema: unknown, via: string): void { - const method = (schema as MethodSchema).shape.method.value; - if (this._registeredMethods.has(method)) { - throw new Error( - `Handler for "${method}" already registered (via ${via}). ` + - `Use addEventListener() to attach multiple listeners, ` + - `or the on* setter for replace semantics.`, - ); + private _ensureEventSlot( + event: K, + ): EventSlot { + let slot = this._eventSlots.get(event) as + | EventSlot + | undefined; + if (!slot) { + slot = { listeners: [] }; + this._eventSlots.set(event, slot as EventSlot); } - this._registeredMethods.add(method); + return slot; } } diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 4c5baa3..00babc4 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -28,7 +28,7 @@ const config = { ], excludePrivate: true, excludeInternal: false, - intentionallyNotExported: ["MethodSchema", "RequestHandlerExtra"], + intentionallyNotExported: ["RequestHandlerExtra"], blockTags: [...OptionDefaults.blockTags, "@description"], jsDocCompatibility: { exampleTag: false, From 59e6ffc6d8822f5139efc4d09fae46350761ebd2 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 15:02:25 -0700 Subject: [PATCH 08/45] refactor: migrate App to SDK v2 client --- src/app-bridge.test.ts | 44 +++--- src/app.test.ts | 300 +++++++++++++++++++++++++++++++++++++++++ src/app.ts | 261 ++++++++++++++++------------------- 3 files changed, 434 insertions(+), 171 deletions(-) create mode 100644 src/app.test.ts diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index 7adbb16..8f432b4 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -7,10 +7,7 @@ import { ListPromptsResultSchema, ListResourcesResultSchema, ListResourceTemplatesResultSchema, - PromptListChangedNotificationSchema, ReadResourceResultSchema, - ResourceListChangedNotificationSchema, - ToolListChangedNotificationSchema, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod/v4"; @@ -2313,9 +2310,12 @@ describe("App <-> AppBridge integration", () => { it("sendToolListChanged sends notification to app", async () => { const receivedNotifications: unknown[] = []; - app.setNotificationHandler(ToolListChangedNotificationSchema, (n) => { - receivedNotifications.push(n.params); - }); + app.setNotificationHandler( + "notifications/tools/list_changed", + (notification) => { + receivedNotifications.push(notification.params); + }, + ); await bridge.connect(bridgeTransport); await app.connect(appTransport); @@ -2328,9 +2328,12 @@ describe("App <-> AppBridge integration", () => { it("sendResourceListChanged sends notification to app", async () => { const receivedNotifications: unknown[] = []; - app.setNotificationHandler(ResourceListChangedNotificationSchema, (n) => { - receivedNotifications.push(n.params); - }); + app.setNotificationHandler( + "notifications/resources/list_changed", + (notification) => { + receivedNotifications.push(notification.params); + }, + ); await bridge.connect(bridgeTransport); await app.connect(appTransport); @@ -2343,9 +2346,12 @@ describe("App <-> AppBridge integration", () => { it("sendPromptListChanged sends notification to app", async () => { const receivedNotifications: unknown[] = []; - app.setNotificationHandler(PromptListChangedNotificationSchema, (n) => { - receivedNotifications.push(n.params); - }); + app.setNotificationHandler( + "notifications/prompts/list_changed", + (notification) => { + receivedNotifications.push(notification.params); + }, + ); await bridge.connect(bridgeTransport); await app.connect(appTransport); @@ -2771,20 +2777,6 @@ describe("isToolVisibilityAppOnly", () => { ); }).toThrow(/already registered/); }); - - it("direct setNotificationHandler throws for event-mapped methods", () => { - const app2 = new App(testAppInfo, {}, { autoResize: false }); - app2.addEventListener("toolinput", () => {}); - expect(() => { - app2.setNotificationHandler( - // @ts-expect-error — exercising throw path with raw schema - { - shape: { method: { value: "ui/notifications/tool-input" } }, - }, - () => {}, - ); - }).toThrow(/already registered/); - }); }); }); diff --git a/src/app.test.ts b/src/app.test.ts new file mode 100644 index 0000000..dcf2d52 --- /dev/null +++ b/src/app.test.ts @@ -0,0 +1,300 @@ +import { afterEach, describe, expect, it } from "bun:test"; +import { + InMemoryTransport, + Server, + type JSONRPCMessage, +} from "@modelcontextprotocol/server"; +import { z } from "zod/v4"; + +import { App } from "./app"; +import { + LATEST_PROTOCOL_VERSION, + McpUiInitializeRequestSchema, + McpUiInitializeResultSchema, + McpUiInitializedNotificationSchema, +} from "./types"; + +const INNER_MCP_PROTOCOL_VERSION = "2025-11-25"; + +type ConnectedPair = { + app: App; + server: Server; + sentByApp: JSONRPCMessage[]; + appsInitialized: Promise; +}; + +async function connectPair( + app = new App( + { name: "view-app", version: "1.0.0" }, + { tools: { listChanged: true } }, + { autoResize: false }, + ), +): Promise { + const server = new Server( + { name: "inner-mcp-server", version: "0.0.0" }, + { + capabilities: { resources: {}, tools: {} }, + supportedProtocolVersions: [INNER_MCP_PROTOCOL_VERSION], + }, + ); + const [appTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + const sentByApp: JSONRPCMessage[] = []; + const send = appTransport.send.bind(appTransport); + appTransport.send = async (message, options) => { + sentByApp.push(message); + await send(message, options); + }; + + let resolveAppsInitialized!: () => void; + const appsInitialized = new Promise((resolve) => { + resolveAppsInitialized = resolve; + }); + + server.setRequestHandler( + "ui/initialize", + { + params: McpUiInitializeRequestSchema.shape.params, + result: McpUiInitializeResultSchema, + }, + (params) => { + expect(params.protocolVersion).toBe(LATEST_PROTOCOL_VERSION); + return { + protocolVersion: LATEST_PROTOCOL_VERSION, + hostCapabilities: { openLinks: {} }, + hostInfo: { name: "apps-host", version: "2.0.0" }, + hostContext: { theme: "dark" as const, locale: "en-US" }, + }; + }, + ); + server.setNotificationHandler( + "ui/notifications/initialized", + { params: McpUiInitializedNotificationSchema.shape.params }, + () => resolveAppsInitialized(), + ); + server.setRequestHandler("tools/call", (request) => ({ + content: [{ type: "text", text: `called ${request.params.name}` }], + })); + server.setRequestHandler("resources/read", (request) => ({ + contents: [{ uri: request.params.uri, text: "resource body" }], + })); + server.setRequestHandler("resources/list", () => ({ + resources: [{ uri: "test://resource", name: "Test resource" }], + })); + server.setRequestHandler("sampling/createMessage", () => ({ + model: "test-model", + role: "assistant", + content: { type: "text", text: "sampled" }, + })); + + await server.connect(serverTransport); + await app.connect(appTransport); + await appsInitialized; + + return { app, server, sentByApp, appsInitialized }; +} + +const connected: Array> = []; + +afterEach(async () => { + await Promise.all( + connected + .splice(0) + .flatMap(({ app, server }) => [app.close(), server.close()]), + ); +}); + +describe("App base MCP SDK v2 Client migration", () => { + it("completes the legacy MCP handshake before the authoritative Apps handshake", async () => { + const pair = await connectPair(); + connected.push(pair); + + const lifecycleMethods = pair.sentByApp + .filter( + (message): message is JSONRPCMessage & { method: string } => + "method" in message, + ) + .map((message) => message.method) + .filter((method) => + [ + "initialize", + "notifications/initialized", + "ui/initialize", + "ui/notifications/initialized", + ].includes(method), + ); + + expect(lifecycleMethods).toEqual([ + "initialize", + "notifications/initialized", + "ui/initialize", + "ui/notifications/initialized", + ]); + expect(pair.app.getNegotiatedProtocolVersion()).toBe( + INNER_MCP_PROTOCOL_VERSION, + ); + }); + + it("uses ui/initialize as the authority for Apps host state", async () => { + const pair = await connectPair(); + connected.push(pair); + + expect(pair.app.getServerVersion()).toEqual({ + name: "inner-mcp-server", + version: "0.0.0", + }); + expect(pair.app.getHostVersion()).toEqual({ + name: "apps-host", + version: "2.0.0", + }); + expect(pair.app.getHostCapabilities()).toEqual({ openLinks: {} }); + expect(pair.app.getHostContext()).toEqual({ + theme: "dark", + locale: "en-US", + }); + }); + + it("preserves registered App tools with the base MCP SDK v2 Client handler context", async () => { + const pair = await connectPair(); + connected.push(pair); + let receivedSignal: AbortSignal | undefined; + + pair.app.registerTool( + "greet", + { description: "Greets a person" }, + async (extra) => { + receivedSignal = extra.mcpReq.signal; + return { content: [{ type: "text", text: "hello" }] }; + }, + ); + + const listed = await pair.server.request({ + method: "tools/list", + params: {}, + }); + const result = await pair.server.request({ + method: "tools/call", + params: { name: "greet", arguments: {} }, + }); + + expect(listed.tools.map((tool) => tool.name)).toEqual(["greet"]); + expect(result.content).toEqual([{ type: "text", text: "hello" }]); + expect(receivedSignal).toBeInstanceOf(AbortSignal); + }); + + it("uses base MCP SDK v2 method-keyed sends for standard MCP requests", async () => { + const pair = await connectPair(); + connected.push(pair); + + const toolResult = await pair.app.callServerTool({ + name: "refresh", + arguments: {}, + }); + const resource = await pair.app.readServerResource({ + uri: "test://resource", + }); + const resources = await pair.app.listServerResources(); + const sample = await pair.app.createSamplingMessage({ + messages: [{ role: "user", content: { type: "text", text: "hello" } }], + maxTokens: 20, + }); + + expect(toolResult.content).toEqual([ + { type: "text", text: "called refresh" }, + ]); + expect(resource.contents[0]).toEqual({ + uri: "test://resource", + text: "resource body", + }); + expect(resources.resources.map(({ uri }) => uri)).toEqual([ + "test://resource", + ]); + expect(sample.content).toEqual({ type: "text", text: "sampled" }); + }); + + it("preserves reconnect behavior after close", async () => { + const first = await connectPair(); + await first.app.close(); + await first.server.close(); + + const second = await connectPair(first.app); + connected.push(second); + + expect(second.app.getNegotiatedProtocolVersion()).toBe( + INNER_MCP_PROTOCOL_VERSION, + ); + expect(second.app.getHostVersion()).toEqual({ + name: "apps-host", + version: "2.0.0", + }); + }); + + it("preserves auto-resize setup after Apps initialization", async () => { + const app = new App( + { name: "view-app", version: "1.0.0" }, + {}, + { autoResize: true }, + ); + let setupCalls = 0; + app.setupSizeChangedNotifications = () => { + setupCalls += 1; + return () => {}; + }; + + const pair = await connectPair(app); + connected.push(pair); + + expect(setupCalls).toBe(1); + }); + + it("preserves strict late-handler timing errors", async () => { + const app = new App( + { name: "view-app", version: "1.0.0" }, + {}, + { autoResize: false, strict: true }, + ); + const pair = await connectPair(app); + connected.push(pair); + + expect(() => { + app.ontoolinput = () => {}; + }).toThrow(/handler registered after connect/); + }); + + it("uses base MCP SDK replacement semantics for request handlers", async () => { + const pair = await connectPair(); + connected.push(pair); + let handledBy = 0; + + pair.app.setRequestHandler("ping", () => { + handledBy = 1; + return {}; + }); + pair.app.setRequestHandler("ping", () => { + handledBy = 2; + return {}; + }); + + await pair.server.request({ method: "ping", params: {} }); + expect(handledBy).toBe(2); + }); + + it("uses base MCP SDK replacement semantics for non-event notifications", async () => { + const pair = await connectPair(); + connected.push(pair); + const calls: number[] = []; + const params = z.object({}); + + pair.app.setNotificationHandler("test/notification", { params }, () => { + calls.push(1); + }); + pair.app.setNotificationHandler("test/notification", { params }, () => { + calls.push(2); + }); + await pair.server.notification({ + method: "test/notification", + params: {}, + }); + + expect(calls).toEqual([2]); + }); +}); diff --git a/src/app.ts b/src/app.ts index c586c31..3c8f5b7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,38 +1,27 @@ import { + Client, + type ClientContext, + type ClientOptions, type RequestOptions, - mergeCapabilities, - Protocol, - ProtocolOptions, -} from "@modelcontextprotocol/sdk/shared/protocol.js"; - -import { - CallToolRequest, - CallToolRequestSchema, - CallToolResult, - CallToolResultSchema, - CreateMessageRequest, - CreateMessageResult, - CreateMessageResultSchema, - CreateMessageResultWithTools, - CreateMessageResultWithToolsSchema, - EmptyResultSchema, - Implementation, - ListResourcesRequest, - ListResourcesResult, - ListResourcesResultSchema, - ListToolsRequest, - ListToolsRequestSchema, - ListToolsResult, - LoggingMessageNotification, - PingRequestSchema, - ReadResourceRequest, - ReadResourceResult, - ReadResourceResultSchema, - Tool, - ToolAnnotations, - ToolListChangedNotification, -} from "@modelcontextprotocol/sdk/types.js"; -import { AppNotification, AppRequest, AppResult } from "./types"; + type CallToolRequest, + type CallToolResult, + type CreateMessageRequest, + type CreateMessageResult, + type CreateMessageResultWithTools, + type Implementation, + type ListResourcesRequest, + type ListResourcesResult, + type ListToolsRequest, + type ListToolsResult, + type LoggingMessageNotification, + type ReadResourceRequest, + type ReadResourceResult, + type Tool, + type ToolAnnotations, + type ToolListChangedNotification, + type Transport, +} from "@modelcontextprotocol/client"; +import { EmptyResultSchema } from "@modelcontextprotocol/core"; import { EventDispatcher } from "./events"; export { EventDispatcher } from "./events"; import { PostMessageTransport } from "./message-transport"; @@ -56,6 +45,7 @@ import { McpUiResourceTeardownRequest, McpUiResourceTeardownRequestSchema, McpUiResourceTeardownResult, + McpUiResourceTeardownResultSchema, McpUiRequestTeardownNotification, McpUiSizeChangedNotification, McpUiToolCancelledNotification, @@ -69,15 +59,36 @@ import { McpUiRequestDisplayModeRequest, McpUiRequestDisplayModeResultSchema, } from "./types"; -import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { StandardSchemaV1, standardSchemaToJsonSchema, validateStandardSchema, } from "./standard-schema"; -import { z, type ZodLiteral, type ZodObject } from "zod/v4"; +import { z, type ZodLiteral, type ZodObject, type ZodType } from "zod/v4"; -type MethodSchema = ZodObject<{ method: ZodLiteral }>; +type MethodSchema = ZodObject<{ + method: ZodLiteral; + params: ZodType; +}>; + +const INNER_MCP_PROTOCOL_VERSION = "2025-11-25"; + +function mergeAppCapabilities( + base: McpUiAppCapabilities, + additional: McpUiAppCapabilities, +): McpUiAppCapabilities { + const result = { ...base, ...additional }; + if (additional.experimental !== undefined) { + result.experimental = { + ...base.experimental, + ...additional.experimental, + }; + } + if (additional.tools !== undefined) { + result.tools = { ...base.tools, ...additional.tools }; + } + return result; +} export type { StandardSchemaV1, @@ -167,7 +178,10 @@ export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; * * @see `ProtocolOptions` from @modelcontextprotocol/sdk for inherited options */ -export type AppOptions = ProtocolOptions & { +export type AppOptions = Omit< + ClientOptions, + "capabilities" | "supportedProtocolVersions" | "versionNegotiation" +> & { /** * Automatically report size changes to the host using `ResizeObserver`. * @@ -207,9 +221,7 @@ export type AppOptions = ProtocolOptions & { allowUnsafeEval?: boolean; }; -type RequestHandlerExtra = Parameters< - Parameters[1] ->[1]; +type RequestHandlerExtra = ClientContext; /** * Result of an app-registered tool callback. When `Out` is provided, @@ -343,38 +355,15 @@ export type AppEventMap = { * await app.connect(); * ``` */ -export class App extends Protocol { +export class App extends Client { private _hostCapabilities?: McpUiHostCapabilities; private _hostInfo?: Implementation; private _hostContext?: McpUiHostContext; private _registeredTools: { [name: string]: RegisteredAppTool } = {}; private _initializedSent = false; - private readonly _registeredMethods = new Set(); private readonly _registeredEvents = new Set(); private readonly _events = new EventDispatcher(); - // ProtocolWithEvents previously owned these registration guards. Keep them - // on App to preserve the existing double-registration behavior while event - // fan-out moves to protocol-independent composition. Arrow-function fields - // ensure Protocol's constructor uses its own methods before the guards init. - override setRequestHandler: Protocol< - AppRequest, - AppNotification, - AppResult - >["setRequestHandler"] = (schema, handler) => { - this._assertMethodNotRegistered(schema, "setRequestHandler"); - super.setRequestHandler(schema, handler); - }; - - override setNotificationHandler: Protocol< - AppRequest, - AppNotification, - AppResult - >["setNotificationHandler"] = (schema, handler) => { - this._assertMethodNotRegistered(schema, "setNotificationHandler"); - super.setNotificationHandler(schema, handler); - }; - /** * Warn if a host-bound method is called before {@link connect `connect`} has * completed the `ui/initialize` → `ui/notifications/initialized` handshake. @@ -479,13 +468,17 @@ export class App extends Protocol { if (this._registeredEvents.has(event)) return; const schema = this.eventSchemas[event]; if (!schema) throw new Error(`Unknown event: ${String(event)}`); - this.setNotificationHandler(schema, (notification) => { - const params = (notification as { params: AppEventMap[K] }).params; - if (event === "hostcontextchanged") { - this._hostContext = { ...this._hostContext, ...params }; - } - this._events.dispatch(event, params); - }); + this.setNotificationHandler( + schema.shape.method.value, + { params: schema.shape.params }, + (params) => { + const eventParams = params as AppEventMap[K]; + if (event === "hostcontextchanged") { + this._hostContext = { ...this._hostContext, ...eventParams }; + } + this._events.dispatch(event, eventParams); + }, + ); this._registeredEvents.add(event); } @@ -508,33 +501,11 @@ export class App extends Protocol { } } - protected replaceRequestHandler: Protocol< - AppRequest, - AppNotification, - AppResult - >["setRequestHandler"] = (schema, handler) => { - const method = (schema as MethodSchema).shape.method.value; - this._registeredMethods.add(method); - super.setRequestHandler(schema, handler); - }; - - private _assertMethodNotRegistered(schema: unknown, via: string): void { - const method = (schema as MethodSchema).shape.method.value; - if (this._registeredMethods.has(method)) { - throw new Error( - `Handler for "${method}" already registered (via ${via}). ` + - `Use addEventListener() to attach multiple listeners, ` + - `or the on* setter for replace semantics.`, - ); - } - this._registeredMethods.add(method); - } - /** * Create a new MCP App instance. * * @param _appInfo - App identification (name and version) - * @param _capabilities - Features and capabilities this app provides + * @param _appCapabilities - Features and capabilities this app provides * @param options - Configuration options including `autoResize` behavior * * @example @@ -548,16 +519,29 @@ export class App extends Protocol { */ constructor( private _appInfo: Implementation, - private _capabilities: McpUiAppCapabilities = {}, + private _appCapabilities: McpUiAppCapabilities = {}, private options: AppOptions = { autoResize: true }, ) { - super(options); + // TEMPORARY INNER-CHANNEL NEGOTIATION: + // App and AppBridge use the public v2 Client/Server roles, so the iframe + // channel first completes the SDK's standard legacy MCP initialization. + // ui/initialize remains authoritative for Apps capabilities, identity, and + // host context, and ui/notifications/initialized remains the View-ready gate. + // Keep the inner MCP era pinned to 2025-11-25. Revisit this sequence only + // after the MCP Apps negotiation specification is resolved: + // https://github.com/modelcontextprotocol/ext-apps/issues/708 + super(_appInfo, { + ...options, + capabilities: {}, + versionNegotiation: { mode: "legacy" }, + supportedProtocolVersions: [INNER_MCP_PROTOCOL_VERSION], + }); if (!options.allowUnsafeEval) { z.config({ jitless: true }); } - this.setRequestHandler(PingRequestSchema, (request) => { + this.setRequestHandler("ping", (request) => { console.log("Received ping:", request.params); return {}; }); @@ -567,13 +551,16 @@ export class App extends Protocol { this.setEventHandler("hostcontextchanged", undefined); } - private registerCapabilities(capabilities: McpUiAppCapabilities): void { + private registerAppCapabilities(capabilities: McpUiAppCapabilities): void { if (this.transport) { throw new Error( "Cannot register capabilities after transport is established", ); } - this._capabilities = mergeCapabilities(this._capabilities, capabilities); + this._appCapabilities = mergeAppCapabilities( + this._appCapabilities, + capabilities, + ); } registerTool< @@ -596,7 +583,7 @@ export class App extends Protocol { } const app = this; const notify = () => { - if (app._initializedSent && app._capabilities.tools?.listChanged) { + if (app._initializedSent && app._appCapabilities.tools?.listChanged) { void app.sendToolListChanged(); } }; @@ -668,8 +655,8 @@ export class App extends Protocol { // to declare { tools: {} } in the constructor just to use registerTool. // Only do this pre-connect; post-connect the capability was already // advertised (or wasn't) and can't change. - if (!this._capabilities.tools && !this.transport) { - this.registerCapabilities({ tools: { listChanged: true } }); + if (!this._appCapabilities.tools && !this.transport) { + this.registerAppCapabilities({ tools: { listChanged: true } }); } this.ensureToolHandlersInitialized(); @@ -1081,11 +1068,15 @@ export class App extends Protocol { ) { this.warnIfRequestHandlerReplaced("onteardown", this._onteardown, callback); this._onteardown = callback; - this.replaceRequestHandler( - McpUiResourceTeardownRequestSchema, - (request, extra) => { + this.setRequestHandler( + "ui/resource-teardown", + { + params: McpUiResourceTeardownRequestSchema.shape.params, + result: McpUiResourceTeardownResultSchema, + }, + (params, extra) => { if (!this._onteardown) throw new Error("No onteardown handler set"); - return this._onteardown(request.params, extra); + return this._onteardown(params, extra); }, ); } @@ -1135,7 +1126,7 @@ export class App extends Protocol { ) { this.warnIfRequestHandlerReplaced("oncalltool", this._oncalltool, callback); this._oncalltool = callback; - this.replaceRequestHandler(CallToolRequestSchema, (request, extra) => { + this.setRequestHandler("tools/call", (request, extra) => { if (!this._oncalltool) throw new Error("No oncalltool handler set"); return this._oncalltool(request.params, extra); }); @@ -1206,7 +1197,7 @@ export class App extends Protocol { callback, ); this._onlisttools = callback; - this.replaceRequestHandler(ListToolsRequestSchema, (request, extra) => { + this.setRequestHandler("tools/list", (request, extra) => { if (!this._onlisttools) throw new Error("No onlisttools handler set"); return this._onlisttools(request.params, extra); }); @@ -1216,7 +1207,7 @@ export class App extends Protocol { * Verify that the host supports the capability required for the given request method. * @internal */ - assertCapabilityForMethod(method: AppRequest["method"]): void { + protected override assertCapabilityForMethod(method: string): void { switch (method) { case "sampling/createMessage": if (!this._hostCapabilities?.sampling) { @@ -1232,11 +1223,11 @@ export class App extends Protocol { * Verify that the app declared the capability required for the given request method. * @internal */ - assertRequestHandlerCapability(method: AppRequest["method"]): void { + protected override assertRequestHandlerCapability(method: string): void { switch (method) { case "tools/call": case "tools/list": - if (!this._capabilities.tools) { + if (!this._appCapabilities.tools) { throw new Error( `Client does not support tool capability (required for ${method})`, ); @@ -1254,26 +1245,10 @@ export class App extends Protocol { * Verify that the app supports the capability required for the given notification method. * @internal */ - assertNotificationCapability(_method: AppNotification["method"]): void { + protected override assertNotificationCapability(_method: string): void { // TODO } - /** - * Verify that task creation is supported for the given request method. - * @internal - */ - protected assertTaskCapability(_method: string): void { - throw new Error("Tasks are not supported in MCP Apps"); - } - - /** - * Verify that task handler is supported for the given method. - * @internal - */ - protected assertTaskHandlerCapability(_method: string): void { - throw new Error("Task handlers are not supported in MCP Apps"); - } - /** * Call a tool on the originating MCP server (proxied through the host). * @@ -1322,7 +1297,6 @@ export class App extends Protocol { } return await this.request( { method: "tools/call", params }, - CallToolResultSchema, { // Hosts may interpose long-running or user-interactive steps before the // tool result arrives. Opting in here lets a host heartbeat keep the @@ -1379,11 +1353,7 @@ export class App extends Protocol { options?: RequestOptions, ): Promise { this._assertInitialized("readServerResource"); - return await this.request( - { method: "resources/read", params }, - ReadResourceResultSchema, - options, - ); + return await this.request({ method: "resources/read", params }, options); } /** @@ -1427,11 +1397,7 @@ export class App extends Protocol { options?: RequestOptions, ): Promise { this._assertInitialized("listServerResources"); - return await this.request( - { method: "resources/list", params }, - ListResourcesResultSchema, - options, - ); + return await this.request({ method: "resources/list", params }, options); } /** @@ -1508,12 +1474,8 @@ export class App extends Protocol { options?: RequestOptions, ): Promise { this._assertInitialized("createSamplingMessage"); - const resultSchema = params.tools - ? CreateMessageResultWithToolsSchema - : CreateMessageResultSchema; return await this.request( { method: "sampling/createMessage", params }, - resultSchema, options, ); } @@ -2030,14 +1992,23 @@ export class App extends Protocol { ); } this._initializedSent = false; - await super.connect(transport); + // TEMPORARY INNER-CHANNEL NEGOTIATION: + // App and AppBridge use the public v2 Client/Server roles, so the iframe + // channel first completes the SDK's standard legacy MCP initialization. + // ui/initialize remains authoritative for Apps capabilities, identity, and + // host context, and ui/notifications/initialized remains the View-ready gate. + // Keep the inner MCP era pinned to 2025-11-25. Revisit this sequence only + // after the MCP Apps negotiation specification is resolved: + // https://github.com/modelcontextprotocol/ext-apps/issues/708 try { + await super.connect(transport); + const result = await this.request( { method: "ui/initialize", params: { - appCapabilities: this._capabilities, + appCapabilities: this._appCapabilities, appInfo: this._appInfo, protocolVersion: LATEST_PROTOCOL_VERSION, }, From d19a1fb83ade586d6e2eb46535e7d84a7f7468a8 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 15:34:33 -0700 Subject: [PATCH 09/45] refactor: migrate AppBridge to SDK v2 server --- src/app-bridge.test.ts | 336 ++++++++++++++++++++++---- src/app-bridge.ts | 527 ++++++++++++++++++++--------------------- 2 files changed, 546 insertions(+), 317 deletions(-) diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index 8f432b4..81e072f 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -1,14 +1,6 @@ import { describe, it, expect, beforeEach, afterEach, spyOn } from "bun:test"; -import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; -import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import type { ServerCapabilities } from "@modelcontextprotocol/sdk/types.js"; -import { - EmptyResultSchema, - ListPromptsResultSchema, - ListResourcesResultSchema, - ListResourceTemplatesResultSchema, - ReadResourceResultSchema, -} from "@modelcontextprotocol/sdk/types.js"; +import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; +import { Server, type ServerCapabilities } from "@modelcontextprotocol/server"; import { z } from "zod/v4"; import { App } from "./app"; @@ -19,6 +11,7 @@ import { getToolUiResourceUri, isToolVisibilityModelOnly, isToolVisibilityAppOnly, + McpUiOpenLinkResultSchema, type McpUiHostCapabilities, } from "./app-bridge"; @@ -31,11 +24,18 @@ const flush = () => new Promise((resolve) => setTimeout(resolve, 0)); */ function createMockClient( serverCapabilities: ServerCapabilities = {}, -): Pick { +): Pick< + Client, + | "getServerCapabilities" + | "request" + | "notification" + | "setNotificationHandler" +> { return { getServerCapabilities: () => serverCapabilities, request: async () => ({}) as never, notification: async () => {}, + setNotificationHandler: () => {}, }; } @@ -82,6 +82,58 @@ describe("App <-> AppBridge integration", () => { expect(initializedFired).toBe(true); }); + it("keeps standard initialization separate from the Apps-ready gate", async () => { + const methods: string[] = []; + let releaseAppsInitialized!: () => void; + let reachedAppsInitialized!: () => void; + const appsInitializedGate = new Promise((resolve) => { + releaseAppsInitialized = resolve; + }); + const reachedGate = new Promise((resolve) => { + reachedAppsInitialized = resolve; + }); + const originalSend = appTransport.send.bind(appTransport); + appTransport.send = async (message, options) => { + if ("method" in message) methods.push(message.method); + if ( + "method" in message && + message.method === "ui/notifications/initialized" + ) { + reachedAppsInitialized(); + await appsInitializedGate; + } + return originalSend(message, options); + }; + + let singularCalls = 0; + let listenerCalls = 0; + bridge.oninitialized = () => singularCalls++; + bridge.addEventListener("initialized", () => listenerCalls++); + + await bridge.connect(bridgeTransport); + const connecting = app.connect(appTransport); + await reachedGate; + + expect(methods).toEqual([ + "initialize", + "notifications/initialized", + "ui/initialize", + "ui/notifications/initialized", + ]); + expect(app.getNegotiatedProtocolVersion()).toBe("2025-11-25"); + expect(bridge.getNegotiatedProtocolVersion()).toBe("2025-11-25"); + expect(bridge.getClientVersion()).toEqual(testAppInfo); + expect(bridge.getAppVersion()).toEqual(testAppInfo); + expect(singularCalls).toBe(0); + expect(listenerCalls).toBe(0); + + releaseAppsInitialized(); + await connecting; + + expect(singularCalls).toBe(1); + expect(listenerCalls).toBe(1); + }); + it("App receives host info and capabilities after connect", async () => { await bridge.connect(bridgeTransport); await app.connect(appTransport); @@ -91,6 +143,8 @@ describe("App <-> AppBridge integration", () => { const hostCaps = app.getHostCapabilities(); expect(hostCaps).toEqual(testHostCapabilities); + expect(bridge.getHostCapabilities()).toEqual(testHostCapabilities); + expect(bridge.getCapabilities()).toEqual({}); }); it("Bridge receives app info and capabilities after initialization", async () => { @@ -611,6 +665,30 @@ describe("App <-> AppBridge integration", () => { expect(result.isError).toBe(true); }); + + it("rejects invalid params for a custom ui request", async () => { + bridge.onopenlink = async () => ({}); + await app.connect(appTransport); + + await expect( + app.request( + { + method: "ui/open-link", + params: { url: 42 }, + }, + McpUiOpenLinkResultSchema, + ), + ).rejects.toThrow(); + }); + + it("rejects invalid results from a custom ui handler", async () => { + bridge.onopenlink = async () => ({ isError: "yes" }) as never; + await app.connect(appTransport); + + await expect( + app.openLink({ url: "https://example.com" }), + ).rejects.toThrow(); + }); }); describe("deprecated method aliases", () => { @@ -680,10 +758,7 @@ describe("App <-> AppBridge integration", () => { await app.connect(appTransport); // Bridge can send ping via the protocol's request method - const result = await bridge.request( - { method: "ping", params: {} }, - EmptyResultSchema, - ); + const result = await bridge.request({ method: "ping", params: {} }); expect(result).toEqual({}); }); @@ -798,7 +873,7 @@ describe("App <-> AppBridge integration", () => { }); await bridge.callTool({ name: "noargs", arguments: {} }); expect(receivedExtra).toBeDefined(); - expect(receivedExtra.signal).toBeInstanceOf(AbortSignal); + expect(receivedExtra.mcpReq.signal).toBeInstanceOf(AbortSignal); }); it("isError result skips output schema validation", async () => { @@ -2180,10 +2255,10 @@ describe("App <-> AppBridge integration", () => { await app.connect(appTransport); // App sends resources/list request via the protocol's request method - const result = await app.request( - { method: "resources/list", params: requestParams }, - ListResourcesResultSchema, - ); + const result = await app.request({ + method: "resources/list", + params: requestParams, + }); expect(receivedRequests).toHaveLength(1); expect(receivedRequests[0]).toMatchObject(requestParams); @@ -2224,10 +2299,10 @@ describe("App <-> AppBridge integration", () => { await bridge.connect(bridgeTransport); await app.connect(appTransport); - const result = await app.request( - { method: "resources/read", params: requestParams }, - ReadResourceResultSchema, - ); + const result = await app.request({ + method: "resources/read", + params: requestParams, + }); expect(receivedRequests).toHaveLength(1); expect(receivedRequests[0]).toMatchObject(requestParams); @@ -2275,10 +2350,10 @@ describe("App <-> AppBridge integration", () => { await bridge.connect(bridgeTransport); await app.connect(appTransport); - const result = await app.request( - { method: "resources/templates/list", params: requestParams }, - ListResourceTemplatesResultSchema, - ); + const result = await app.request({ + method: "resources/templates/list", + params: requestParams, + }); expect(receivedRequests).toHaveLength(1); expect(receivedRequests[0]).toMatchObject(requestParams); @@ -2298,10 +2373,10 @@ describe("App <-> AppBridge integration", () => { await bridge.connect(bridgeTransport); await app.connect(appTransport); - const result = await app.request( - { method: "prompts/list", params: requestParams }, - ListPromptsResultSchema, - ); + const result = await app.request({ + method: "prompts/list", + params: requestParams, + }); expect(receivedRequests).toHaveLength(1); expect(receivedRequests[0]).toMatchObject(requestParams); @@ -2310,6 +2385,7 @@ describe("App <-> AppBridge integration", () => { it("sendToolListChanged sends notification to app", async () => { const receivedNotifications: unknown[] = []; + bridge.oncalltool = async () => ({ content: [] }); app.setNotificationHandler( "notifications/tools/list_changed", (notification) => { @@ -2328,6 +2404,7 @@ describe("App <-> AppBridge integration", () => { it("sendResourceListChanged sends notification to app", async () => { const receivedNotifications: unknown[] = []; + bridge.onlistresources = async () => ({ resources: [] }); app.setNotificationHandler( "notifications/resources/list_changed", (notification) => { @@ -2346,6 +2423,7 @@ describe("App <-> AppBridge integration", () => { it("sendPromptListChanged sends notification to app", async () => { const receivedNotifications: unknown[] = []; + bridge.onlistprompts = async () => ({ prompts: [] }); app.setNotificationHandler( "notifications/prompts/list_changed", (notification) => { @@ -2364,6 +2442,184 @@ describe("App <-> AppBridge integration", () => { }); }); +describe("AppBridge outer v2 Client proxy", () => { + let outerClient: Client; + let outerServer: Server; + let outerClientTransport: InMemoryTransport; + let outerServerTransport: InMemoryTransport; + let outerInitializeCount: number; + + beforeEach(async () => { + [outerClientTransport, outerServerTransport] = + InMemoryTransport.createLinkedPair(); + outerInitializeCount = 0; + const originalSend = outerClientTransport.send.bind(outerClientTransport); + outerClientTransport.send = async (message, options) => { + if ("method" in message && message.method === "initialize") { + outerInitializeCount++; + } + return originalSend(message, options); + }; + + outerServer = new Server( + { name: "ActualServer", version: "1.0.0" }, + { + capabilities: { + tools: { listChanged: true }, + resources: { listChanged: true }, + prompts: { listChanged: true }, + }, + supportedProtocolVersions: ["2025-11-25"], + }, + ); + outerServer.setRequestHandler("tools/call", async (request) => ({ + content: [ + { + type: "text", + text: `outer:${request.params.name}`, + }, + ], + })); + + outerClient = new Client( + { name: "HostOuterClient", version: "1.0.0" }, + { + capabilities: {}, + versionNegotiation: { mode: "legacy" }, + supportedProtocolVersions: ["2025-11-25"], + }, + ); + await outerServer.connect(outerServerTransport); + await outerClient.connect(outerClientTransport); + }); + + afterEach(async () => { + await outerClient.close().catch(() => {}); + await outerServer.close().catch(() => {}); + }); + + it("proxies through the outer Client without reconnecting it", async () => { + const bridge = new AppBridge( + outerClient, + testHostInfo, + testHostCapabilities, + ); + const app = new App(testAppInfo, {}, { autoResize: false }); + let [appTransport, bridgeTransport] = InMemoryTransport.createLinkedPair(); + + await bridge.connect(bridgeTransport); + await app.connect(appTransport); + expect( + await app.callServerTool({ name: "first", arguments: {} }), + ).toMatchObject({ + content: [{ type: "text", text: "outer:first" }], + }); + expect(outerInitializeCount).toBe(1); + + await app.close(); + await bridge.close(); + [appTransport, bridgeTransport] = InMemoryTransport.createLinkedPair(); + await bridge.connect(bridgeTransport); + await app.connect(appTransport); + + expect( + await app.callServerTool({ name: "second", arguments: {} }), + ).toMatchObject({ + content: [{ type: "text", text: "outer:second" }], + }); + expect(outerInitializeCount).toBe(1); + expect(outerClient.getServerVersion()).toMatchObject({ + name: "ActualServer", + version: "1.0.0", + }); + + await app.close(); + await bridge.close(); + }); + + it("propagates View cancellation to the actual outer Server", async () => { + let started!: () => void; + let aborted!: () => void; + const startedPromise = new Promise((resolve) => { + started = resolve; + }); + const abortedPromise = new Promise((resolve) => { + aborted = resolve; + }); + outerServer.setRequestHandler("tools/call", async (_request, context) => { + started(); + await new Promise((resolve) => { + context.mcpReq.signal.addEventListener( + "abort", + () => { + aborted(); + resolve(); + }, + { once: true }, + ); + }); + return { content: [] }; + }); + + const bridge = new AppBridge( + outerClient, + testHostInfo, + testHostCapabilities, + ); + const app = new App(testAppInfo, {}, { autoResize: false }); + const [appTransport, bridgeTransport] = + InMemoryTransport.createLinkedPair(); + await bridge.connect(bridgeTransport); + await app.connect(appTransport); + + const controller = new AbortController(); + const result = app.callServerTool( + { name: "cancel-me", arguments: {} }, + { signal: controller.signal }, + ); + await startedPromise; + controller.abort("test cancellation"); + await abortedPromise; + await expect(result).rejects.toThrow(); + + await app.close(); + await bridge.close(); + }); + + it("forwards outer list-changed notifications to the App", async () => { + const bridge = new AppBridge( + outerClient, + testHostInfo, + testHostCapabilities, + ); + const app = new App(testAppInfo, {}, { autoResize: false }); + const [appTransport, bridgeTransport] = + InMemoryTransport.createLinkedPair(); + const received: string[] = []; + app.setNotificationHandler("notifications/tools/list_changed", () => { + received.push("tools"); + }); + app.setNotificationHandler("notifications/resources/list_changed", () => { + received.push("resources"); + }); + app.setNotificationHandler("notifications/prompts/list_changed", () => { + received.push("prompts"); + }); + + await bridge.connect(bridgeTransport); + await app.connect(appTransport); + await outerServer.sendToolListChanged(); + await outerServer.sendResourceListChanged(); + await outerServer.sendPromptListChanged(); + await flush(); + + expect(received).toEqual(["tools", "resources", "prompts"]); + + await app.close(); + await bridge.close(); + }); +}); + describe("getToolUiResourceUri", () => { describe("new nested format (_meta.ui.resourceUri)", () => { it("extracts resourceUri from _meta.ui.resourceUri", () => { @@ -2758,24 +3014,16 @@ describe("isToolVisibilityAppOnly", () => { expect(app.onteardown).toBe(handler); }); - it("direct setRequestHandler throws when called twice", () => { + it("direct setRequestHandler uses base SDK replacement semantics", () => { const bridge2 = new AppBridge( createMockClient() as Client, testHostInfo, testHostCapabilities, ); - bridge2.setRequestHandler( - // @ts-expect-error — exercising throw path with raw schema - { shape: { method: { value: "test/method" } } }, - () => ({}), - ); + bridge2.setRequestHandler("ping", () => ({})); expect(() => { - bridge2.setRequestHandler( - // @ts-expect-error — exercising throw path with raw schema - { shape: { method: { value: "test/method" } } }, - () => ({}), - ); - }).toThrow(/already registered/); + bridge2.setRequestHandler("ping", () => ({})); + }).not.toThrow(); }); }); }); diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 18d7671..c65510d 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -1,61 +1,51 @@ -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; +import { type Client } from "@modelcontextprotocol/client"; import { - CallToolRequest, - CallToolRequestSchema, - CallToolResult, - CallToolResultSchema, - CreateMessageRequest, - CreateMessageRequestSchema, - CreateMessageResult, - CreateMessageResultWithTools, - EmptyResult, - Implementation, - ListPromptsRequest, - ListPromptsRequestSchema, - ListPromptsResult, - ListPromptsResultSchema, - ListResourcesRequest, - ListResourcesRequestSchema, - ListResourcesResult, - ListResourcesResultSchema, - ListResourceTemplatesRequest, - ListResourceTemplatesRequestSchema, - ListResourceTemplatesResult, - ListResourceTemplatesResultSchema, - ListToolsRequest, - ListToolsRequestSchema, - ListToolsResultSchema, - LoggingMessageNotification, + Server, + type CallToolRequest, + type CallToolResult, + type CreateMessageRequest, + type CreateMessageResult, + type CreateMessageResultWithTools, + type EmptyResult, + type Implementation, + type JSONRPCRequest, + type ListPromptsRequest, + type ListPromptsResult, + type ListResourcesRequest, + type ListResourcesResult, + type ListResourceTemplatesRequest, + type ListResourceTemplatesResult, + type ListToolsRequest, + type LoggingMessageNotification, + type PingRequest, + type PromptListChangedNotification, + type ReadResourceRequest, + type ReadResourceResult, + type RequestOptions, + type ResourceListChangedNotification, + type Result, + type ServerCapabilities, + type ServerContext, + type ServerOptions, + type Tool, + type ToolListChangedNotification, + type Transport, +} from "@modelcontextprotocol/server"; +import { + EmptyResultSchema, LoggingMessageNotificationSchema, - PingRequest, - PingRequestSchema, - PromptListChangedNotification, - PromptListChangedNotificationSchema, - ReadResourceRequest, - ReadResourceRequestSchema, - ReadResourceResult, - ReadResourceResultSchema, - ResourceListChangedNotification, - ResourceListChangedNotificationSchema, - Tool, - ToolListChangedNotification, - ToolListChangedNotificationSchema, -} from "@modelcontextprotocol/sdk/types.js"; -import { - Protocol, - ProtocolOptions, - RequestOptions, -} from "@modelcontextprotocol/sdk/shared/protocol.js"; +} from "@modelcontextprotocol/core"; import { EventDispatcher } from "./events"; -import type { ZodLiteral, ZodObject } from "zod/v4"; +import type { ZodLiteral, ZodObject, ZodType } from "zod/v4"; -type MethodSchema = ZodObject<{ method: ZodLiteral }>; +type MethodSchema = ZodObject<{ + method: ZodLiteral; + params: ZodType; +}>; + +const INNER_MCP_PROTOCOL_VERSION = "2025-11-25"; import { - type AppNotification, - type AppRequest, - type AppResult, type McpUiSandboxResourceReadyNotification, type McpUiSizeChangedNotification, type McpUiToolCancelledNotification, @@ -74,15 +64,19 @@ import { McpUiInitializeRequest, McpUiInitializeRequestSchema, McpUiInitializeResult, + McpUiInitializeResultSchema, McpUiMessageRequest, McpUiMessageRequestSchema, McpUiMessageResult, + McpUiMessageResultSchema, McpUiOpenLinkRequest, McpUiOpenLinkRequestSchema, McpUiOpenLinkResult, + McpUiOpenLinkResultSchema, McpUiDownloadFileRequest, McpUiDownloadFileRequestSchema, McpUiDownloadFileResult, + McpUiDownloadFileResultSchema, McpUiResourceTeardownRequest, McpUiResourceTeardownResultSchema, McpUiRequestTeardownNotification, @@ -93,6 +87,7 @@ import { McpUiRequestDisplayModeRequest, McpUiRequestDisplayModeRequestSchema, McpUiRequestDisplayModeResult, + McpUiRequestDisplayModeResultSchema, McpUiResourcePermissions, McpUiToolMeta, } from "./types"; @@ -101,6 +96,20 @@ export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./app"; import { RESOURCE_URI_META_KEY } from "./app"; export { PostMessageTransport } from "./message-transport"; +function proxiedInnerCapabilities( + capabilities: ServerCapabilities, +): ServerCapabilities { + return { + ...(capabilities.tools && { tools: capabilities.tools }), + ...(capabilities.resources && { + resources: { + listChanged: capabilities.resources.listChanged, + }, + }), + ...(capabilities.prompts && { prompts: capabilities.prompts }), + }; +} + /** * Extract UI resource URI from tool metadata. * @@ -206,10 +215,13 @@ export function buildAllowAttribute( * * @property hostContext - Optional initial host context to provide to the view * - * @see `ProtocolOptions` from @modelcontextprotocol/sdk for available options + * @see `ServerOptions` from @modelcontextprotocol/server for available options * @see {@link McpUiHostContext `McpUiHostContext`} for the hostContext structure */ -export type HostOptions = ProtocolOptions & { +export type HostOptions = Omit< + ServerOptions, + "capabilities" | "supportedProtocolVersions" +> & { hostContext?: McpUiHostContext; }; @@ -224,15 +236,12 @@ export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION]; /** * Extra metadata passed to request handlers. * - * This type represents the additional context provided by the `Protocol` class - * when handling requests, including abort signals and session information. - * It is extracted from the MCP SDK's request handler signature. + * This type represents the additional context provided by the base MCP SDK + * `Server` when handling requests. * * @internal */ -type RequestHandlerExtra = Parameters< - Parameters[1] ->[1]; +type RequestHandlerExtra = ServerContext; /** * Maps DOM-style event names to their notification `params` types. @@ -306,66 +315,53 @@ export type AppBridgeEventMap = { * await bridge.connect(transport); * ``` */ -export class AppBridge extends Protocol { +export class AppBridge extends Server { private _appCapabilities?: McpUiAppCapabilities; private _hostContext: McpUiHostContext = {}; private _appInfo?: Implementation; private _initializedReceived = false; - private readonly _registeredMethods = new Set(); private readonly _registeredEvents = new Set(); private readonly _events = new EventDispatcher(); - // ProtocolWithEvents previously owned these registration guards. Keep them - // on AppBridge to preserve the existing double-registration behavior while - // event fan-out moves to protocol-independent composition. Arrow-function - // fields let Protocol's constructor run before the guards initialize. - override setRequestHandler: Protocol< - AppRequest, - AppNotification, - AppResult - >["setRequestHandler"] = (schema, handler) => { - this._assertMethodNotRegistered(schema, "setRequestHandler"); - super.setRequestHandler(schema, handler); - }; - - override setNotificationHandler: Protocol< - AppRequest, - AppNotification, - AppResult - >["setNotificationHandler"] = (schema, handler) => { - this._assertMethodNotRegistered(schema, "setNotificationHandler"); - super.setNotificationHandler(schema, handler); - }; - /** - * Wrap every handler registered via `replaceRequestHandler` with a check - * that the View has sent `ui/notifications/initialized`. Warns (never - * throws) so lenient hosts keep working while still surfacing the - * misordering that leaves strict hosts with a permanently hidden iframe. - * `ui/initialize` and `ping` use `setRequestHandler` directly and are - * intentionally exempt. + * The base MCP SDK calls this hook for every standard and custom request + * handler registration. AppBridge uses it to preserve the existing warning + * when a View calls host methods before `ui/notifications/initialized`, + * without adding overload-forwarding adapters around `setRequestHandler`. + * Chaining through `super._wrapHandler()` retains the base `Server`'s own + * role-specific behavior, including tool-result validation. The two + * initialization methods and `ping` are exempt because they are valid before + * the Apps-ready gate. This wraps registered callbacks, not JSON-RPC dispatch. * * @see {@link https://github.com/anthropics/claude-ai-mcp/issues/149 claude-ai-mcp#149} */ - protected replaceRequestHandler: Protocol< - AppRequest, - AppNotification, - AppResult - >["setRequestHandler"] = (schema, handler) => { - const method = (schema as MethodSchema).shape.method.value; - this._registeredMethods.add(method); - super.setRequestHandler(schema, (request, extra) => { + protected override _wrapHandler( + method: string, + handler: ( + request: JSONRPCRequest, + context: ServerContext, + ) => Promise, + ): (request: JSONRPCRequest, context: ServerContext) => Promise { + const wrapped = super._wrapHandler(method, handler); + if ( + method === "initialize" || + method === "ui/initialize" || + method === "ping" + ) { + return wrapped; + } + return async (request, context) => { if (!this._initializedReceived) { console.warn( - `[ext-apps] AppBridge received '${request.method}' before ` + + `[ext-apps] AppBridge received '${method}' before ` + `ui/notifications/initialized. The View is calling host ` + `methods before completing the handshake; it should await ` + `app.connect() first.`, ); } - return handler(request, extra); - }); - }; + return wrapped(request, context); + }; + } private readonly eventSchemas: { [K in keyof AppBridgeEventMap]: MethodSchema; @@ -406,17 +402,27 @@ export class AppBridge extends Protocol this._events.removeEventListener(event, handler); } - private _ensureEventSlot( - event: K, - ): void { + private _ensureEventSlot(event: K): void { if (this._registeredEvents.has(event)) return; const schema = this.eventSchemas[event]; if (!schema) throw new Error(`Unknown event: ${String(event)}`); - this.setNotificationHandler(schema, (notification) => { - const params = (notification as { params: AppBridgeEventMap[K] }).params; + + const dispatch = (params: AppBridgeEventMap[K]) => { if (event === "initialized") this._initializedReceived = true; this._events.dispatch(event, params); - }); + }; + + if (event === "loggingmessage") { + this.setNotificationHandler("notifications/message", (notification) => { + dispatch(notification.params as AppBridgeEventMap[K]); + }); + } else { + this.setNotificationHandler( + schema.shape.method.value, + { params: schema.shape.params }, + (params) => dispatch(params as AppBridgeEventMap[K]), + ); + } this._registeredEvents.add(event); } @@ -433,16 +439,12 @@ export class AppBridge extends Protocol } } - private _assertMethodNotRegistered(schema: unknown, via: string): void { - const method = (schema as MethodSchema).shape.method.value; - if (this._registeredMethods.has(method)) { - throw new Error( - `Handler for "${method}" already registered (via ${via}). ` + - `Use addEventListener() to attach multiple listeners, ` + - `or the on* setter for replace semantics.`, - ); - } - this._registeredMethods.add(method); + private ensureInnerCapabilities(capabilities: ServerCapabilities): void { + const current = super.getCapabilities(); + const missing = Object.keys(capabilities).some( + (key) => current[key as keyof ServerCapabilities] === undefined, + ); + if (missing) this.registerCapabilities(capabilities); } /** @@ -453,8 +455,8 @@ export class AppBridge extends Protocol * between the View and the server. When `null`, you must register handlers * manually using the {@link oncalltool `oncalltool`}, {@link onlistresources `onlistresources`}, etc. setters. * @param _hostInfo - Host application identification (name and version) - * @param _capabilities - Features and capabilities the host supports - * @param options - Configuration options (inherited from Protocol) + * @param _hostCapabilities - Features and capabilities the host supports + * @param options - Configuration options (inherited from Server) * * @example With MCP client (automatic forwarding) * ```ts source="./app-bridge.examples.ts#AppBridge_constructor_withMcpClient" @@ -481,29 +483,61 @@ export class AppBridge extends Protocol constructor( private _client: Client | null, private _hostInfo: Implementation, - private _capabilities: McpUiHostCapabilities, + private _hostCapabilities: McpUiHostCapabilities, options?: HostOptions, ) { - super(options); + // TEMPORARY INNER-CHANNEL NEGOTIATION: + // App and AppBridge use the public v2 Client/Server roles, so the iframe + // channel first completes the SDK's standard legacy MCP initialization. + // ui/initialize remains authoritative for Apps capabilities, identity, and + // host context, and ui/notifications/initialized remains the View-ready gate. + // Keep the inner MCP era pinned to 2025-11-25. Revisit this sequence only + // after the MCP Apps negotiation specification is resolved: + // https://github.com/modelcontextprotocol/ext-apps/issues/708 + super(_hostInfo, { + ...options, + capabilities: {}, + supportedProtocolVersions: [INNER_MCP_PROTOCOL_VERSION], + }); + + // The base Server's standard initialized callback is MCP-ready state. The + // public AppBridge callback remains exclusively the Apps View-ready gate. + super.setNotificationHandler("notifications/initialized", () => {}); + Object.defineProperty(this, "oninitialized", { + configurable: true, + enumerable: true, + get: () => this.getEventHandler("initialized"), + set: (callback: AppBridge["oninitialized"]) => + this.setEventHandler("initialized", callback), + }); this._ensureEventSlot("initialized"); this._hostContext = options?.hostContext || {}; - this.setRequestHandler(McpUiInitializeRequestSchema, (request) => - this._oninitialize(request), + this.setRequestHandler( + "ui/initialize", + { + params: McpUiInitializeRequestSchema.shape.params, + result: McpUiInitializeResultSchema, + }, + (params) => this._onAppsInitialize(params), ); - this.setRequestHandler(PingRequestSchema, (request, extra) => { + this.setRequestHandler("ping", (request, extra) => { this.onping?.(request.params, extra); return {}; }); // Default handler for requestDisplayMode - returns current mode from host context. // Hosts can override this by setting bridge.onrequestdisplaymode = ... - this.replaceRequestHandler( - McpUiRequestDisplayModeRequestSchema, - (request) => { + this.setRequestHandler( + "ui/request-display-mode", + { + params: McpUiRequestDisplayModeRequestSchema.shape.params, + result: McpUiRequestDisplayModeResultSchema, + }, + () => { const currentMode = this._hostContext.displayMode ?? "inline"; return { mode: currentMode }; }, @@ -680,18 +714,9 @@ export class AppBridge extends Protocol * @see {@link sendToolInput `sendToolInput`} for sending tool arguments to the View * @deprecated Use {@link addEventListener `addEventListener("initialized", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}. */ - get oninitialized(): - | ((params: McpUiInitializedNotification["params"]) => void) - | undefined { - return this.getEventHandler("initialized"); - } - set oninitialized( - callback: - | ((params: McpUiInitializedNotification["params"]) => void) - | undefined, - ) { - this.setEventHandler("initialized", callback); - } + override oninitialized?: ( + params?: McpUiInitializedNotification["params"], + ) => void; /** * Register a handler for message requests from the view. @@ -744,11 +769,15 @@ export class AppBridge extends Protocol ) { this.warnIfRequestHandlerReplaced("onmessage", this._onmessage, callback); this._onmessage = callback; - this.replaceRequestHandler( - McpUiMessageRequestSchema, - async (request, extra) => { + this.setRequestHandler( + "ui/message", + { + params: McpUiMessageRequestSchema.shape.params, + result: McpUiMessageResultSchema, + }, + async (params, extra) => { if (!this._onmessage) throw new Error("No onmessage handler set"); - return this._onmessage(request.params, extra); + return this._onmessage(params, extra); }, ); } @@ -813,11 +842,15 @@ export class AppBridge extends Protocol ) { this.warnIfRequestHandlerReplaced("onopenlink", this._onopenlink, callback); this._onopenlink = callback; - this.replaceRequestHandler( - McpUiOpenLinkRequestSchema, - async (request, extra) => { + this.setRequestHandler( + "ui/open-link", + { + params: McpUiOpenLinkRequestSchema.shape.params, + result: McpUiOpenLinkResultSchema, + }, + async (params, extra) => { if (!this._onopenlink) throw new Error("No onopenlink handler set"); - return this._onopenlink(request.params, extra); + return this._onopenlink(params, extra); }, ); } @@ -885,12 +918,16 @@ export class AppBridge extends Protocol callback, ); this._ondownloadfile = callback; - this.replaceRequestHandler( - McpUiDownloadFileRequestSchema, - async (request, extra) => { + this.setRequestHandler( + "ui/download-file", + { + params: McpUiDownloadFileRequestSchema.shape.params, + result: McpUiDownloadFileResultSchema, + }, + async (params, extra) => { if (!this._ondownloadfile) throw new Error("No ondownloadfile handler set"); - return this._ondownloadfile(request.params, extra); + return this._ondownloadfile(params, extra); }, ); } @@ -985,12 +1022,16 @@ export class AppBridge extends Protocol callback, ); this._onrequestdisplaymode = callback; - this.replaceRequestHandler( - McpUiRequestDisplayModeRequestSchema, - async (request, extra) => { + this.setRequestHandler( + "ui/request-display-mode", + { + params: McpUiRequestDisplayModeRequestSchema.shape.params, + result: McpUiRequestDisplayModeResultSchema, + }, + async (params, extra) => { if (!this._onrequestdisplaymode) throw new Error("No onrequestdisplaymode handler set"); - return this._onrequestdisplaymode(request.params, extra); + return this._onrequestdisplaymode(params, extra); }, ); } @@ -1082,12 +1123,16 @@ export class AppBridge extends Protocol callback, ); this._onupdatemodelcontext = callback; - this.replaceRequestHandler( - McpUiUpdateModelContextRequestSchema, - async (request, extra) => { + this.setRequestHandler( + "ui/update-model-context", + { + params: McpUiUpdateModelContextRequestSchema.shape.params, + result: EmptyResultSchema, + }, + async (params, extra) => { if (!this._onupdatemodelcontext) throw new Error("No onupdatemodelcontext handler set"); - return this._onupdatemodelcontext(request.params, extra); + return this._onupdatemodelcontext(params, extra); }, ); } @@ -1135,13 +1180,11 @@ export class AppBridge extends Protocol ) { this.warnIfRequestHandlerReplaced("oncalltool", this._oncalltool, callback); this._oncalltool = callback; - this.replaceRequestHandler( - CallToolRequestSchema, - async (request, extra) => { - if (!this._oncalltool) throw new Error("No oncalltool handler set"); - return this._oncalltool(request.params, extra); - }, - ); + this.ensureInnerCapabilities({ tools: {} }); + this.setRequestHandler("tools/call", async (request, extra) => { + if (!this._oncalltool) throw new Error("No oncalltool handler set"); + return this._oncalltool(request.params, extra); + }); } /** @@ -1179,12 +1222,9 @@ export class AppBridge extends Protocol extra: RequestHandlerExtra, ) => Promise, ) { - this.setRequestHandler( - CreateMessageRequestSchema, - async (request, extra) => { - return callback(request.params, extra); - }, - ); + this.setRequestHandler("sampling/createMessage", async (request, extra) => { + return callback(request.params, extra); + }); } /** @@ -1260,14 +1300,12 @@ export class AppBridge extends Protocol callback, ); this._onlistresources = callback; - this.replaceRequestHandler( - ListResourcesRequestSchema, - async (request, extra) => { - if (!this._onlistresources) - throw new Error("No onlistresources handler set"); - return this._onlistresources(request.params, extra); - }, - ); + this.ensureInnerCapabilities({ resources: {} }); + this.setRequestHandler("resources/list", async (request, extra) => { + if (!this._onlistresources) + throw new Error("No onlistresources handler set"); + return this._onlistresources(request.params, extra); + }); } /** @@ -1317,8 +1355,9 @@ export class AppBridge extends Protocol callback, ); this._onlistresourcetemplates = callback; - this.replaceRequestHandler( - ListResourceTemplatesRequestSchema, + this.ensureInnerCapabilities({ resources: {} }); + this.setRequestHandler( + "resources/templates/list", async (request, extra) => { if (!this._onlistresourcetemplates) throw new Error("No onlistresourcetemplates handler set"); @@ -1374,14 +1413,12 @@ export class AppBridge extends Protocol callback, ); this._onreadresource = callback; - this.replaceRequestHandler( - ReadResourceRequestSchema, - async (request, extra) => { - if (!this._onreadresource) - throw new Error("No onreadresource handler set"); - return this._onreadresource(request.params, extra); - }, - ); + this.ensureInnerCapabilities({ resources: {} }); + this.setRequestHandler("resources/read", async (request, extra) => { + if (!this._onreadresource) + throw new Error("No onreadresource handler set"); + return this._onreadresource(request.params, extra); + }); } /** @@ -1459,14 +1496,11 @@ export class AppBridge extends Protocol callback, ); this._onlistprompts = callback; - this.replaceRequestHandler( - ListPromptsRequestSchema, - async (request, extra) => { - if (!this._onlistprompts) - throw new Error("No onlistprompts handler set"); - return this._onlistprompts(request.params, extra); - }, - ); + this.ensureInnerCapabilities({ prompts: {} }); + this.setRequestHandler("prompts/list", async (request, extra) => { + if (!this._onlistprompts) throw new Error("No onlistprompts handler set"); + return this._onlistprompts(request.params, extra); + }); } /** @@ -1495,46 +1529,6 @@ export class AppBridge extends Protocol }); } - /** - * Verify that the guest supports the capability required for the given request method. - * @internal - */ - assertCapabilityForMethod(method: AppRequest["method"]): void { - // TODO - } - - /** - * Verify that a request handler is registered and supported for the given method. - * @internal - */ - assertRequestHandlerCapability(method: AppRequest["method"]): void { - // TODO - } - - /** - * Verify that the host supports the capability required for the given notification method. - * @internal - */ - assertNotificationCapability(method: AppNotification["method"]): void { - // TODO - } - - /** - * Verify that task creation is supported for the given request method. - * @internal - */ - protected assertTaskCapability(_method: string): void { - throw new Error("Tasks are not supported in MCP Apps"); - } - - /** - * Verify that task handler is supported for the given method. - * @internal - */ - protected assertTaskHandlerCapability(_method: string): void { - throw new Error("Task handlers are not supported in MCP Apps"); - } - /** * Get the host capabilities passed to the constructor. * @@ -1542,18 +1536,18 @@ export class AppBridge extends Protocol * * @see {@link McpUiHostCapabilities `McpUiHostCapabilities`} for the capabilities structure */ - getCapabilities(): McpUiHostCapabilities { - return this._capabilities; + getHostCapabilities(): McpUiHostCapabilities { + return this._hostCapabilities; } /** * Handle the ui/initialize request from the guest. * @internal */ - private async _oninitialize( - request: McpUiInitializeRequest, + private async _onAppsInitialize( + params: McpUiInitializeRequest["params"], ): Promise { - const requestedVersion = request.params.protocolVersion; + const requestedVersion = params.protocolVersion; if (this._appInfo !== undefined) { console.warn( @@ -1564,8 +1558,8 @@ export class AppBridge extends Protocol ); } - this._appCapabilities = request.params.appCapabilities; - this._appInfo = request.params.appInfo; + this._appCapabilities = params.appCapabilities; + this._appInfo = params.appInfo; const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes( requestedVersion, @@ -1575,7 +1569,7 @@ export class AppBridge extends Protocol return { protocolVersion, - hostCapabilities: this.getCapabilities(), + hostCapabilities: this.getHostCapabilities(), hostInfo: this._hostInfo, hostContext: this._hostContext, }; @@ -1857,11 +1851,7 @@ export class AppBridge extends Protocol * @returns Promise resolving to the tool call result */ callTool(params: CallToolRequest["params"], options?: RequestOptions) { - return this.request( - { method: "tools/call", params }, - CallToolResultSchema, - options, - ); + return this.request({ method: "tools/call", params }, options); } /** @@ -1874,11 +1864,7 @@ export class AppBridge extends Protocol * @returns Promise resolving to the list of tools */ listTools(params: ListToolsRequest["params"], options?: RequestOptions) { - return this.request( - { method: "tools/list", params }, - ListToolsResultSchema, - options, - ); + return this.request({ method: "tools/list", params }, options); } /** @@ -1949,18 +1935,18 @@ export class AppBridge extends Protocol if (!serverCapabilities) { throw new Error("Client server capabilities not available"); } + this.registerCapabilities(proxiedInnerCapabilities(serverCapabilities)); if (serverCapabilities.tools) { this.oncalltool = async (params, extra) => { return this._client!.request( { method: "tools/call", params }, - CallToolResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; if (serverCapabilities.tools.listChanged) { this._client.setNotificationHandler( - ToolListChangedNotificationSchema, + "notifications/tools/list_changed", (n) => this.sendToolListChanged(n.params), ); } @@ -1969,27 +1955,24 @@ export class AppBridge extends Protocol this.onlistresources = async (params, extra) => { return this._client!.request( { method: "resources/list", params }, - ListResourcesResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; this.onlistresourcetemplates = async (params, extra) => { return this._client!.request( { method: "resources/templates/list", params }, - ListResourceTemplatesResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; this.onreadresource = async (params, extra) => { return this._client!.request( { method: "resources/read", params }, - ReadResourceResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; if (serverCapabilities.resources.listChanged) { this._client.setNotificationHandler( - ResourceListChangedNotificationSchema, + "notifications/resources/list_changed", (n) => this.sendResourceListChanged(n.params), ); } @@ -1998,22 +1981,20 @@ export class AppBridge extends Protocol this.onlistprompts = async (params, extra) => { return this._client!.request( { method: "prompts/list", params }, - ListPromptsResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; if (serverCapabilities.prompts.listChanged) { this._client.setNotificationHandler( - PromptListChangedNotificationSchema, + "notifications/prompts/list_changed", (n) => this.sendPromptListChanged(n.params), ); } } } - // MCP-UI specific handlers are registered by the host component - // after the proxy is created. The standard MCP initialization - // (via oninitialized callback set in constructor) handles the ready signal. + // The standard MCP handshake only establishes the temporary inner Server + // role. ui/notifications/initialized remains the public View-ready signal. return super.connect(transport); } From a76e3a76913b9482a5d00342184004ccaab44885 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 15:46:56 -0700 Subject: [PATCH 10/45] refactor: migrate shared SDK surfaces to v2 --- scripts/generate-schemas.ts | 6 +- src/generated/schema.json | 182 +++++++++++++++++++++++------------- src/generated/schema.ts | 2 +- src/message-transport.ts | 8 +- src/react/useApp.tsx | 3 +- src/server/index.ts | 54 +++-------- src/spec.types.ts | 22 ++--- src/types.ts | 130 -------------------------- 8 files changed, 149 insertions(+), 258 deletions(-) diff --git a/scripts/generate-schemas.ts b/scripts/generate-schemas.ts index 109f352..85225f5 100644 --- a/scripts/generate-schemas.ts +++ b/scripts/generate-schemas.ts @@ -12,7 +12,7 @@ * * ts-to-zod generates `import { z } from "zod"`. We rewrite this to * `import { z } from "zod/v4"` because the generated schemas compose with - * schemas imported from `@modelcontextprotocol/sdk/types.js`, which the SDK + * schemas imported from `@modelcontextprotocol/core`, which the SDK * constructs via `zod/v4`. Mixing schemas from the v3 and v4 APIs at runtime * fails with errors like `keyValidator._parse is not a function` (v3 internals * calling into v4 objects, or vice versa). The `zod/v4` subpath is exported by @@ -22,7 +22,7 @@ * * **Problem**: ts-to-zod cannot resolve types imported from external packages. * When it encounters types like `ContentBlock`, `CallToolResult`, `Implementation`, - * `RequestId`, and `Tool` from `@modelcontextprotocol/sdk`, it generates `z.any()` + * `RequestId`, and `Tool` from `@modelcontextprotocol/core`, it generates `z.any()` * as a placeholder. * * **Solution**: Import the schemas from MCP SDK and remove the z.any() placeholders. @@ -193,7 +193,7 @@ function postProcess(content: string): string { `import { z } from "zod/v4"; import { ${mcpImports}, -} from "@modelcontextprotocol/sdk/types.js";`, +} from "@modelcontextprotocol/core";`, ); // 2. Remove z.any() placeholders for external types (now imported from MCP SDK) diff --git a/src/generated/schema.json b/src/generated/schema.json index 80b4ac6..5f2124a 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -596,7 +596,9 @@ "propertyNames": { "type": "string" }, - "additionalProperties": {} + "additionalProperties": { + "$ref": "#/$defs/__schema0" + } }, "required": { "type": "array", @@ -611,25 +613,10 @@ "outputSchema": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "object" - }, - "properties": { - "type": "object", - "propertyNames": { - "type": "string" - }, - "additionalProperties": {} - }, - "required": { - "type": "array", - "items": { - "type": "string" - } + "$schema": { + "type": "string" } }, - "required": ["type"], "additionalProperties": {} }, "annotations": { @@ -1286,7 +1273,40 @@ } }, "required": ["method", "params"], - "additionalProperties": false + "additionalProperties": false, + "$defs": { + "__schema0": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/$defs/__schema0" + } + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/__schema0" + } + } + ] + } + } }, "McpUiHostContext": { "$schema": "https://json-schema.org/draft/2020-12/schema", @@ -1359,7 +1379,9 @@ "propertyNames": { "type": "string" }, - "additionalProperties": {} + "additionalProperties": { + "$ref": "#/$defs/__schema0" + } }, "required": { "type": "array", @@ -1374,25 +1396,10 @@ "outputSchema": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "object" - }, - "properties": { - "type": "object", - "propertyNames": { - "type": "string" - }, - "additionalProperties": {} - }, - "required": { - "type": "array", - "items": { - "type": "string" - } + "$schema": { + "type": "string" } }, - "required": ["type"], "additionalProperties": {} }, "annotations": { @@ -2044,7 +2051,40 @@ "additionalProperties": false } }, - "additionalProperties": {} + "additionalProperties": {}, + "$defs": { + "__schema0": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/$defs/__schema0" + } + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/__schema0" + } + } + ] + } + } }, "McpUiHostCss": { "$schema": "https://json-schema.org/draft/2020-12/schema", @@ -2942,7 +2982,9 @@ "propertyNames": { "type": "string" }, - "additionalProperties": {} + "additionalProperties": { + "$ref": "#/$defs/__schema0" + } }, "required": { "type": "array", @@ -2957,25 +2999,10 @@ "outputSchema": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "object" - }, - "properties": { - "type": "object", - "propertyNames": { - "type": "string" - }, - "additionalProperties": {} - }, - "required": { - "type": "array", - "items": { - "type": "string" - } + "$schema": { + "type": "string" } }, - "required": ["type"], "additionalProperties": {} }, "annotations": { @@ -3637,7 +3664,40 @@ "hostCapabilities", "hostContext" ], - "additionalProperties": {} + "additionalProperties": {}, + "$defs": { + "__schema0": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/$defs/__schema0" + } + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/__schema0" + } + } + ] + } + } }, "McpUiInitializedNotification": { "$schema": "https://json-schema.org/draft/2020-12/schema", @@ -5669,13 +5729,7 @@ ] } }, - "structuredContent": { - "type": "object", - "propertyNames": { - "type": "string" - }, - "additionalProperties": {} - }, + "structuredContent": {}, "isError": { "type": "boolean" } diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 4368737..341c7b4 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -10,7 +10,7 @@ import { RequestIdSchema, ResourceLinkSchema, ToolSchema, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/core"; /** * @description Color theme preference for the host environment. diff --git a/src/message-transport.ts b/src/message-transport.ts index 9d19543..9483540 100644 --- a/src/message-transport.ts +++ b/src/message-transport.ts @@ -1,12 +1,10 @@ -import { +import { JSONRPCMessageSchema } from "@modelcontextprotocol/core"; +import type { JSONRPCMessage, - JSONRPCMessageSchema, MessageExtraInfo, -} from "@modelcontextprotocol/sdk/types.js"; -import { Transport, TransportSendOptions, -} from "@modelcontextprotocol/sdk/shared/transport.js"; +} from "@modelcontextprotocol/client"; import { TOOL_INPUT_PARTIAL_METHOD } from "./spec.types"; /** diff --git a/src/react/useApp.tsx b/src/react/useApp.tsx index 6189246..0e64a74 100644 --- a/src/react/useApp.tsx +++ b/src/react/useApp.tsx @@ -1,6 +1,5 @@ import { useEffect, useState } from "react"; -import { Implementation } from "@modelcontextprotocol/sdk/types.js"; -import { Client } from "@modelcontextprotocol/sdk/client"; +import type { Implementation } from "@modelcontextprotocol/client"; import { App, AppOptions, diff --git a/src/server/index.ts b/src/server/index.ts index c90514a..b8b3ad0 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -40,24 +40,17 @@ import { McpUiClientCapabilities, } from "../app.js"; import type { - BaseToolCallback, + ClientCapabilities, McpServer, + ReadResourceResult, RegisteredTool, ResourceMetadata, + StandardSchemaWithJSON, + ToolAnnotations, ToolCallback, ReadResourceCallback as _ReadResourceCallback, RegisteredResource, -} from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - AnySchema, - ZodRawShapeCompat, -} from "@modelcontextprotocol/sdk/server/zod-compat.js"; -import type { StandardSchemaWithJSON } from "../standard-schema"; -import type { - ClientCapabilities, - ReadResourceResult, - ToolAnnotations, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/server"; // Re-exports for convenience export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE }; @@ -70,8 +63,8 @@ export type { ResourceMetadata, ToolCallback }; export interface ToolConfig { title?: string; description?: string; - inputSchema?: ZodRawShapeCompat | StandardSchemaWithJSON; - outputSchema?: ZodRawShapeCompat | StandardSchemaWithJSON; + inputSchema?: StandardSchemaWithJSON; + outputSchema?: StandardSchemaWithJSON; annotations?: ToolAnnotations; _meta?: Record; } @@ -215,9 +208,8 @@ export interface McpUiAppResourceConfig extends ResourceMetadata { * @see {@link registerAppResource `registerAppResource`} to register the HTML resource referenced by the tool */ export function registerAppTool< - OutputArgs extends ZodRawShapeCompat | StandardSchemaWithJSON, - InputArgs extends undefined | ZodRawShapeCompat | StandardSchemaWithJSON = - undefined, + OutputArgs extends StandardSchemaWithJSON, + InputArgs extends StandardSchemaWithJSON | undefined = undefined, >( server: Pick, name: string, @@ -225,15 +217,7 @@ export function registerAppTool< inputSchema?: InputArgs; outputSchema?: OutputArgs; }, - // The widened constraint signals the v2 API shape, but NOTE: McpServer in - // sdk@1.x still calls zod internals at runtime, so non-zod schemas will fail - // here until we depend on sdk v2. Zod (which all current callers use) is - // unaffected. The cast below bridges the 1.x type signature. - cb: ToolCallback< - InputArgs extends undefined | ZodRawShapeCompat | AnySchema - ? InputArgs - : AnySchema - >, + cb: ToolCallback, ): RegisteredTool { // Normalize metadata for backward compatibility: // - If _meta.ui.resourceUri is set, also set the legacy flat key @@ -251,14 +235,7 @@ export function registerAppTool< normalizedMeta = { ...meta, ui: { ...uiMeta, resourceUri: legacyUri } }; } - // Cast bridges the widened StandardSchemaWithJSON constraint to the - // sdk@1.x zod-typed signature. Drops once we depend on sdk v2. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return server.registerTool( - name, - { ...config, _meta: normalizedMeta } as any, - cb as any, - ); + return server.registerTool(name, { ...config, _meta: normalizedMeta }, cb); } export type McpUiReadResourceResult = ReadResourceResult & { @@ -418,10 +395,6 @@ export const EXTENSION_ID = "io.modelcontextprotocol/ui"; * This helper retrieves the capability object from the `extensions` field * where MCP Apps advertises its support. * - * Note: The `clientCapabilities` parameter extends the SDK's `ClientCapabilities` - * type with an `extensions` field (pending SEP-1724). Once `extensions` is added - * to the SDK, this can use `ClientCapabilities` directly. - * * @param clientCapabilities - The client capabilities from the initialize response * @returns The MCP Apps capability settings, or `undefined` if not supported * @@ -456,10 +429,7 @@ export const EXTENSION_ID = "io.modelcontextprotocol/ui"; * ``` */ export function getUiCapability( - clientCapabilities: - | (ClientCapabilities & { extensions?: Record }) - | null - | undefined, + clientCapabilities: ClientCapabilities | null | undefined, ): McpUiClientCapabilities | undefined { if (!clientCapabilities) { return undefined; diff --git a/src/spec.types.ts b/src/spec.types.ts index 7a8b337..8b03de9 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -18,7 +18,7 @@ import type { RequestId, ResourceLink, Tool, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/client"; /** * Current protocol version supported by this SDK. @@ -163,8 +163,8 @@ export interface McpUiOpenLinkResult { /** @description True if the host failed to open the URL (e.g., due to security policy). */ isError?: boolean; /** - * Index signature required for MCP SDK `Protocol` class compatibility. - * Note: The generated schema uses passthrough() to allow additional properties. + * Additional result properties are preserved for protocol extensibility. + * The generated schema uses passthrough() to allow them. */ [key: string]: unknown; } @@ -195,8 +195,8 @@ export interface McpUiDownloadFileResult { /** @description True if the download failed (e.g., user cancelled or host denied). */ isError?: boolean; /** - * Index signature required for MCP SDK `Protocol` class compatibility. - * Note: The generated schema uses passthrough() to allow additional properties. + * Additional result properties are preserved for protocol extensibility. + * The generated schema uses passthrough() to allow them. */ [key: string]: unknown; } @@ -223,8 +223,8 @@ export interface McpUiMessageResult { /** @description True if the host rejected or failed to deliver the message. */ isError?: boolean; /** - * Index signature required for MCP SDK `Protocol` class compatibility. - * Note: The generated schema uses passthrough() to allow additional properties. + * Additional result properties are preserved for protocol extensibility. + * The generated schema uses passthrough() to allow them. */ [key: string]: unknown; } @@ -454,7 +454,7 @@ export interface McpUiResourceTeardownRequest { */ export interface McpUiResourceTeardownResult { /** - * Index signature required for MCP SDK `Protocol` class compatibility. + * Additional result properties are preserved for protocol extensibility. */ [key: string]: unknown; } @@ -577,8 +577,8 @@ export interface McpUiInitializeResult { /** @description Rich context about the host environment. */ hostContext: McpUiHostContext; /** - * Index signature required for MCP SDK `Protocol` class compatibility. - * Note: The generated schema uses passthrough() to allow additional properties. + * Additional result properties are preserved for protocol extensibility. + * The generated schema uses passthrough() to allow them. */ [key: string]: unknown; } @@ -752,7 +752,7 @@ export interface McpUiRequestDisplayModeResult { /** @description The display mode that was actually set. May differ from requested if not supported. */ mode: McpUiDisplayMode; /** - * Index signature required for MCP SDK `Protocol` class compatibility. + * Additional result properties are preserved for protocol extensibility. * Note: The generated schema uses passthrough() to allow additional properties. */ [key: string]: unknown; diff --git a/src/types.ts b/src/types.ts index 7fc6b71..655812b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -69,33 +69,6 @@ export { type McpUiClientCapabilities, } from "./spec.types.js"; -// Import types needed for protocol type unions (not re-exported, just used internally) -import type { - McpUiInitializeRequest, - McpUiOpenLinkRequest, - McpUiDownloadFileRequest, - McpUiMessageRequest, - McpUiUpdateModelContextRequest, - McpUiResourceTeardownRequest, - McpUiRequestDisplayModeRequest, - McpUiHostContextChangedNotification, - McpUiToolInputNotification, - McpUiToolInputPartialNotification, - McpUiToolResultNotification, - McpUiToolCancelledNotification, - McpUiSandboxResourceReadyNotification, - McpUiInitializedNotification, - McpUiSizeChangedNotification, - McpUiSandboxProxyReadyNotification, - McpUiRequestTeardownNotification, - McpUiInitializeResult, - McpUiOpenLinkResult, - McpUiDownloadFileResult, - McpUiMessageResult, - McpUiResourceTeardownResult, - McpUiRequestDisplayModeResult, -} from "./spec.types.js"; - // Re-export all schemas from generated/schema.ts (already PascalCase) export { McpUiThemeSchema, @@ -135,106 +108,3 @@ export { McpUiToolVisibilitySchema, McpUiToolMetaSchema, } from "./generated/schema.js"; - -// Re-export SDK types used in protocol type unions -import { - CallToolRequest, - CallToolResult, - CreateMessageRequest, - CreateMessageResult, - CreateMessageResultWithTools, - EmptyResult, - ListPromptsRequest, - ListPromptsResult, - ListResourcesRequest, - ListResourcesResult, - ListResourceTemplatesRequest, - ListResourceTemplatesResult, - ListToolsRequest, - ListToolsResult, - LoggingMessageNotification, - PingRequest, - PromptListChangedNotification, - ReadResourceRequest, - ReadResourceResult, - ResourceListChangedNotification, - ToolListChangedNotification, -} from "@modelcontextprotocol/sdk/types.js"; - -/** - * All request types in the MCP Apps protocol. - * - * Includes: - * - MCP UI requests (initialize, open-link, message, resource-teardown, request-display-mode) - * - MCP server requests forwarded from the app (tools/call, tools/list, resources/list, - * resources/templates/list, resources/read, prompts/list) - * - MCP client requests forwarded to the host (sampling/createMessage) - * - Protocol requests (ping) - */ -export type AppRequest = - | McpUiInitializeRequest - | McpUiOpenLinkRequest - | McpUiDownloadFileRequest - | McpUiMessageRequest - | McpUiUpdateModelContextRequest - | McpUiResourceTeardownRequest - | McpUiRequestDisplayModeRequest - | CallToolRequest - | ListToolsRequest - | ListResourcesRequest - | ListResourceTemplatesRequest - | ReadResourceRequest - | ListPromptsRequest - | CreateMessageRequest - | PingRequest; - -/** - * All notification types in the MCP Apps protocol. - * - * Host to app: - * - Tool lifecycle (input, input-partial, result, cancelled) - * - Host context changes - * - MCP list changes (tools, resources, prompts) - * - Sandbox resource ready - * - * App to host: - * - Initialized, size-changed, sandbox-proxy-ready, request-teardown - * - Logging messages - */ -export type AppNotification = - // Sent to app - | McpUiHostContextChangedNotification - | McpUiToolInputNotification - | McpUiToolInputPartialNotification - | McpUiToolResultNotification - | McpUiToolCancelledNotification - | McpUiSandboxResourceReadyNotification - | ToolListChangedNotification - | ResourceListChangedNotification - | PromptListChangedNotification - // Received from app - | McpUiInitializedNotification - | McpUiSizeChangedNotification - | McpUiSandboxProxyReadyNotification - | McpUiRequestTeardownNotification - | LoggingMessageNotification; - -/** - * All result types in the MCP Apps protocol. - */ -export type AppResult = - | McpUiInitializeResult - | McpUiOpenLinkResult - | McpUiDownloadFileResult - | McpUiMessageResult - | McpUiResourceTeardownResult - | McpUiRequestDisplayModeResult - | CallToolResult - | ListToolsResult - | ListResourcesResult - | ListResourceTemplatesResult - | ReadResourceResult - | ListPromptsResult - | CreateMessageResult - | CreateMessageResultWithTools - | EmptyResult; From f94cb38b0b6453476cb45e3edccf606505be7708 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 17:07:30 -0700 Subject: [PATCH 11/45] refactor: finish MCP SDK v2 migration --- AGENTS.md | 8 +- README.md | 10 +- build.bun.ts | 12 +- docs/authorization.md | 2 +- docs/migrate_from_openai_apps.md | 8 +- docs/overview.md | 8 +- docs/patterns.md | 11 +- docs/patterns.tsx | 17 +-- docs/quickstart.md | 19 +-- docs/testing-mcp-apps.md | 4 + examples/basic-host/package.json | 2 +- examples/basic-host/src/implementation.ts | 12 +- examples/basic-host/src/index.tsx | 2 +- examples/basic-server-preact/main.ts | 10 +- examples/basic-server-preact/package.json | 3 +- examples/basic-server-preact/server.ts | 10 +- examples/basic-server-preact/src/mcp-app.tsx | 2 +- examples/basic-server-react/main.ts | 10 +- examples/basic-server-react/package.json | 3 +- examples/basic-server-react/server.ts | 10 +- examples/basic-server-react/src/mcp-app.tsx | 2 +- examples/basic-server-solid/main.ts | 10 +- examples/basic-server-solid/package.json | 3 +- examples/basic-server-solid/server.ts | 10 +- examples/basic-server-solid/src/mcp-app.tsx | 2 +- examples/basic-server-svelte/main.ts | 10 +- examples/basic-server-svelte/package.json | 3 +- examples/basic-server-svelte/server.ts | 10 +- examples/basic-server-svelte/src/App.svelte | 2 +- examples/basic-server-vanillajs/main.ts | 10 +- examples/basic-server-vanillajs/package.json | 3 +- examples/basic-server-vanillajs/server.ts | 9 +- .../basic-server-vanillajs/src/mcp-app.ts | 2 +- examples/basic-server-vue/main.ts | 10 +- examples/basic-server-vue/package.json | 3 +- examples/basic-server-vue/server.ts | 10 +- examples/basic-server-vue/src/App.vue | 2 +- examples/budget-allocator-server/main.ts | 10 +- examples/budget-allocator-server/package.json | 2 +- examples/budget-allocator-server/server.ts | 12 +- examples/cohort-heatmap-server/main.ts | 10 +- examples/cohort-heatmap-server/package.json | 2 +- examples/cohort-heatmap-server/server.ts | 10 +- examples/customer-segmentation-server/main.ts | 10 +- .../customer-segmentation-server/package.json | 2 +- .../customer-segmentation-server/server.ts | 14 +-- examples/debug-server/main.ts | 10 +- examples/debug-server/package.json | 2 +- examples/debug-server/server.ts | 10 +- examples/integration-server/main.ts | 10 +- examples/integration-server/package.json | 3 +- examples/integration-server/server.ts | 14 +-- examples/integration-server/src/mcp-app.tsx | 2 +- examples/lazy-auth-server/package.json | 5 +- examples/lazy-auth-server/server.ts | 29 ++--- examples/map-server/main.ts | 10 +- examples/map-server/package.json | 3 +- examples/map-server/server.ts | 18 +-- examples/map-server/src/mcp-app.ts | 2 +- examples/pdf-server/main.ts | 10 +- examples/pdf-server/package.json | 3 +- examples/pdf-server/server.test.ts | 3 +- examples/pdf-server/server.ts | 57 +++++---- examples/pdf-server/src/mcp-app.ts | 6 +- examples/qr-server/package.json | 3 +- examples/quickstart/main.ts | 10 +- examples/quickstart/package.json | 5 +- examples/quickstart/server.ts | 5 +- examples/say-server/package.json | 3 +- examples/scenario-modeler-server/main.ts | 10 +- examples/scenario-modeler-server/package.json | 2 +- examples/scenario-modeler-server/server.ts | 14 +-- examples/shadertoy-server/main.ts | 10 +- examples/shadertoy-server/package.json | 2 +- examples/shadertoy-server/server.ts | 10 +- examples/sheet-music-server/main.ts | 10 +- examples/sheet-music-server/package.json | 2 +- examples/sheet-music-server/server.ts | 10 +- examples/system-monitor-server/main.ts | 10 +- examples/system-monitor-server/package.json | 2 +- examples/system-monitor-server/server.ts | 18 +-- examples/threejs-server/main.ts | 10 +- examples/threejs-server/package.json | 3 +- examples/threejs-server/server.ts | 12 +- .../threejs-server/src/mcp-app-wrapper.tsx | 2 +- examples/transcript-server/main.ts | 10 +- examples/transcript-server/package.json | 2 +- examples/transcript-server/server.ts | 13 +- examples/video-resource-server/main.ts | 10 +- examples/video-resource-server/package.json | 3 +- examples/video-resource-server/server.ts | 12 +- examples/video-resource-server/src/mcp-app.ts | 2 +- examples/wiki-explorer-server/main.ts | 10 +- examples/wiki-explorer-server/package.json | 3 +- examples/wiki-explorer-server/server.ts | 10 +- examples/wiki-explorer-server/src/mcp-app.ts | 2 +- package-lock.json | 95 +++++++++------ package.json | 2 - .../skills/add-app-to-server/SKILL.md | 111 +++++++++++------- .../mcp-apps/skills/convert-web-app/SKILL.md | 57 +++++---- .../mcp-apps/skills/create-mcp-app/SKILL.md | 5 +- scripts/typedoc-plugin-base-sdk-links.mjs | 16 +++ src/app-bridge.examples.ts | 36 +++--- src/app-bridge.ts | 62 +++++----- src/app.examples.ts | 9 +- src/app.ts | 19 ++- src/events.ts | 5 +- src/message-transport.test.ts | 2 +- src/server/index.examples.ts | 11 +- src/server/index.test.ts | 2 +- src/server/index.ts | 4 +- typedoc.config.mjs | 25 ++++ 112 files changed, 684 insertions(+), 540 deletions(-) create mode 100644 scripts/typedoc-plugin-base-sdk-links.mjs diff --git a/AGENTS.md b/AGENTS.md index 6bfc8cd..f850aa3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,8 +60,8 @@ rm -fR package-lock.json node_modules && \ ### Key Source Files -- `src/app.ts` - `App` class extends MCP Protocol, handles guest initialization, tool calls, messaging -- `src/app-bridge.ts` - `AppBridge` class for hosts, proxies MCP requests, sends tool input/results to guests +- `src/app.ts` - `App` subclasses the base MCP SDK `Client`, handles View initialization, tool calls, and messaging +- `src/app-bridge.ts` - `AppBridge` subclasses the base MCP SDK `Server` for the inner iframe channel and proxies through a separate outer `Client` - `src/server/index.ts` - Helpers for MCP servers to register tools/resources with UI metadata - `src/types.ts` - Protocol types re-exported from `spec.types.ts` and Zod schemas from `generated/schema.ts` (auto-generated during build) - `src/message-transport.ts` - `PostMessageTransport` for iframe communication @@ -70,12 +70,12 @@ rm -fR package-lock.json node_modules && \ ### Protocol Flow ``` -View (App) <--PostMessageTransport--> Host (AppBridge) <--MCP Client--> MCP Server +View (App/Client) <--PostMessageTransport--> Host (AppBridge/Server) <--separate MCP Client--> MCP Server ``` 1. Host creates iframe with view HTML 2. View creates `App` instance and calls `connect()` with `PostMessageTransport` -3. View sends `ui/initialize` request, receives host capabilities and context +3. View and Host complete the temporary inner base MCP handshake, then the View sends `ui/initialize` and receives authoritative Apps capabilities and context 4. Host sends `sendToolInput()` with tool arguments after initialization 5. View can call server tools via `app.callServerTool()` or send messages via `app.sendMessage()` 6. Host sends `sendToolResult()` when tool execution completes diff --git a/README.md b/README.md index 2fd89ff..b3f68e7 100644 --- a/README.md +++ b/README.md @@ -122,9 +122,17 @@ resources: ## Getting Started ```bash -npm install -S @modelcontextprotocol/ext-apps +npm install -S @modelcontextprotocol/ext-apps \ + @modelcontextprotocol/client@2.0.0-beta.4 \ + @modelcontextprotocol/server@2.0.0-beta.4 \ + @modelcontextprotocol/core@2.0.0-beta.4 \ + zod@^4.2.0 ``` +This release uses the split base MCP SDK v2 packages. Install all three at the +exact published beta.4 version so the Apps SDK and your MCP client/server share +one compatible protocol implementation. + **New here?** Start with the [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html) to build your first MCP App. diff --git a/build.bun.ts b/build.bun.ts index a129d5d..837279f 100644 --- a/build.bun.ts +++ b/build.bun.ts @@ -31,9 +31,15 @@ function buildJs( }); } -// zod is a peerDependency — keep it external so consumers share a single -// zod instance (instanceof ZodError / schema.extend() break with duplicate copies). -const PEER_EXTERNALS = ["@modelcontextprotocol/sdk", "zod"]; +// Peer dependencies stay external in the standard entry points so consumers +// share one base MCP SDK and Zod instance. The *-with-deps entry points keep +// bundling these dependencies for standalone browser use. +const PEER_EXTERNALS = [ + "@modelcontextprotocol/client", + "@modelcontextprotocol/core", + "@modelcontextprotocol/server", + "zod", +]; await Promise.all([ buildJs("src/app.ts", { diff --git a/docs/authorization.md b/docs/authorization.md index 59fc26c..df7adc6 100644 --- a/docs/authorization.md +++ b/docs/authorization.md @@ -150,7 +150,7 @@ registerAppTool( "get_account_balance", { description: "Get account balance", - inputSchema: { accountId: z.string() }, + inputSchema: z.object({ accountId: z.string() }), }, async ({ accountId }) => { if (!authInfo) { diff --git a/docs/migrate_from_openai_apps.md b/docs/migrate_from_openai_apps.md index d91281b..22d5d9f 100644 --- a/docs/migrate_from_openai_apps.md +++ b/docs/migrate_from_openai_apps.md @@ -66,7 +66,7 @@ The server-side changes involve updating metadata structure and using helper fun #### Before (OpenAI) ```typescript -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { McpServer } from "@modelcontextprotocol/server"; import { z } from "zod"; function createServer() { @@ -78,7 +78,7 @@ function createServer() { { title: "Shopping Cart", description: "Display the user's shopping cart", - inputSchema: { userId: z.string() }, + inputSchema: z.object({ userId: z.string() }), annotations: { readOnlyHint: true }, _meta: { "openai/outputTemplate": "ui://view/cart.html", @@ -126,7 +126,7 @@ function createServer() { #### After (MCP Apps) ```typescript -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { McpServer } from "@modelcontextprotocol/server"; import { registerAppTool, registerAppResource, @@ -144,7 +144,7 @@ function createServer() { { title: "Shopping Cart", description: "Display the user's shopping cart", - inputSchema: { userId: z.string() }, + inputSchema: z.object({ userId: z.string() }), annotations: { readOnlyHint: true }, _meta: { ui: { resourceUri: "ui://view/cart.html" } }, }, diff --git a/docs/overview.md b/docs/overview.md index 28f5b23..317ab43 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -56,7 +56,11 @@ flowchart LR - **Host** — The chat client (e.g., Claude Desktop) that connects to servers, embeds Views in iframes, and proxies communication between them. - **View** — The UI running inside a sandboxed iframe. It receives tool data from the Host and can call server tools or send messages back to the chat. -The View acts as an MCP client, the Host acts as a proxy, and the Server is a standard MCP server. +The View's `App` subclasses the base MCP SDK `Client`. The Host's `AppBridge` +subclasses the base MCP SDK `Server` for the inner iframe channel, while a +separate outer `Client` connects the Host to the actual MCP Server. Keeping +those two connections separate prevents iframe negotiation and capabilities +from leaking into the server connection. ## Lifecycle @@ -93,7 +97,7 @@ sequenceDiagram ``` 1. **Discovery** — The Host learns about tools and their UI resources when connecting to the server. -2. **Initialization** — When a UI tool is called, the Host renders the iframe. The View sends `ui/initialize` and receives host context (theme, capabilities, container dimensions). This handshake ensures the View is ready before receiving data. +2. **Initialization** — When a UI tool is called, the Host renders the iframe. The SDK first completes its temporary inner base MCP handshake, then the View sends `ui/initialize` and receives host context (theme, capabilities, container dimensions). `ui/initialize` remains authoritative for Apps capabilities and identity, and `ui/notifications/initialized` remains the public View-ready signal. 3. **Data delivery** — The Host sends tool arguments and, once available, tool results to the View. Results include both `content` (text for the model's context) and optionally `structuredContent` (data optimized for UI rendering). This separation lets servers provide rich data to the UI without bloating the model's context. 4. **Interactive phase** — The user interacts with the View. The View can call tools, send messages, or update context. 5. **Teardown** — Before unmounting, the Host notifies the View so it can save state or release resources. diff --git a/docs/patterns.md b/docs/patterns.md index 87a6d82..884de26 100644 --- a/docs/patterns.md +++ b/docs/patterns.md @@ -19,7 +19,7 @@ registerAppTool( "update-quantity", { description: "Update item quantity in cart", - inputSchema: { itemId: z.string(), quantity: z.number() }, + inputSchema: z.object({ itemId: z.string(), quantity: z.number() }), _meta: { ui: { resourceUri: "ui://shop/cart.html", @@ -127,14 +127,14 @@ registerAppTool( { title: "Read Data Bytes", description: "Load binary data in chunks", - inputSchema: { + inputSchema: z.object({ id: z.string().describe("Resource identifier"), offset: z.number().min(0).default(0).describe("Byte offset"), byteCount: z .number() .default(MAX_CHUNK_BYTES) .describe("Bytes to read"), - }, + }), outputSchema: DataChunkSchema, // Hidden from model - only callable by the App _meta: { ui: { visibility: ["app"] } }, @@ -281,10 +281,7 @@ server.registerResource( ```ts source="./patterns.tsx#binaryBlobResourceClient" -const result = await app.request( - { method: "resources/read", params: { uri: `video://${videoId}` } }, - ReadResourceResultSchema, -); +const result = await app.readServerResource({ uri: `video://${videoId}` }); const content = result.contents[0]; if (!content || !("blob" in content)) { diff --git a/docs/patterns.tsx b/docs/patterns.tsx index 6b87692..bb9eaa4 100644 --- a/docs/patterns.tsx +++ b/docs/patterns.tsx @@ -17,16 +17,12 @@ import { randomUUID } from "node:crypto"; import type { CallToolResult, ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; -import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/server"; import type { McpUiHostContext } from "../src/types.js"; import { useEffect, useState } from "react"; import { useApp } from "../src/react/index.js"; import { registerAppTool } from "../src/server/index.js"; -import { - McpServer, - ResourceTemplate, -} from "@modelcontextprotocol/sdk/server/mcp.js"; +import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server"; import { z } from "zod"; /** @@ -117,14 +113,14 @@ function chunkedDataServer(server: McpServer) { { title: "Read Data Bytes", description: "Load binary data in chunks", - inputSchema: { + inputSchema: z.object({ id: z.string().describe("Resource identifier"), offset: z.number().min(0).default(0).describe("Byte offset"), byteCount: z .number() .default(MAX_CHUNK_BYTES) .describe("Bytes to read"), - }, + }), outputSchema: DataChunkSchema, // Hidden from model - only callable by the App _meta: { ui: { visibility: ["app"] } }, @@ -252,10 +248,7 @@ function binaryBlobResourceServer( */ async function binaryBlobResourceClient(app: App, videoId: string) { //#region binaryBlobResourceClient - const result = await app.request( - { method: "resources/read", params: { uri: `video://${videoId}` } }, - ReadResourceResultSchema, - ); + const result = await app.readServerResource({ uri: `video://${videoId}` }); const content = result.contents[0]; if (!content || !("blob" in content)) { diff --git a/docs/quickstart.md b/docs/quickstart.md index 3e00796..eb6b096 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -21,7 +21,7 @@ This tutorial assumes you've built an MCP server before and are comfortable with We'll use the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) to build the server. -You'll also need Node.js 18+. +You'll also need Node.js 20+. ## 1. Set up the project @@ -37,7 +37,7 @@ Install the dependencies you'll need: ```bash npm init -y -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk express cors +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 @modelcontextprotocol/node@2.0.0-beta.4 @modelcontextprotocol/express@2.0.0-beta.4 zod@^4.2.0 express cors npm install -D typescript vite vite-plugin-singlefile @types/express @types/cors @types/node tsx concurrently cross-env ``` @@ -178,9 +178,10 @@ import { registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { McpServer } from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; +import { z } from "zod"; const DIST_DIR = path.join(import.meta.dirname, "dist"); @@ -205,7 +206,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async () => { @@ -240,10 +241,10 @@ export function createServer(): McpServer { ```ts source="../examples/quickstart/main.ts" -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -263,7 +264,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/docs/testing-mcp-apps.md b/docs/testing-mcp-apps.md index df1396c..e5d82b5 100644 --- a/docs/testing-mcp-apps.md +++ b/docs/testing-mcp-apps.md @@ -28,6 +28,10 @@ The [`basic-host`](https://github.com/modelcontextprotocol/ext-apps/tree/main/ex cd examples/basic-host ``` + The root install runs the package build, including type-checking the + documentation snippets against the exact base MCP SDK beta.4 packages. Do + not bypass a failed build before testing the host. + 2. Start basic-host, pointing it to your MCP server: ```bash diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index 1b45dcf..583d2ff 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "2.0.0-beta.4", "react": "^19.2.0", "react-dom": "^19.2.0", "zod": "^4.1.13" diff --git a/examples/basic-host/src/implementation.ts b/examples/basic-host/src/implementation.ts index 31e3698..5f680ff 100644 --- a/examples/basic-host/src/implementation.ts +++ b/examples/basic-host/src/implementation.ts @@ -1,8 +1,12 @@ import { RESOURCE_MIME_TYPE, getToolUiResourceUri, type McpUiSandboxProxyReadyNotification, AppBridge, PostMessageTransport, type McpUiResourceCsp, type McpUiResourcePermissions, buildAllowAttribute, type McpUiUpdateModelContextRequest, type McpUiMessageRequest } from "@modelcontextprotocol/ext-apps/app-bridge"; -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; -import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; -import type { CallToolResult, Resource, Tool } from "@modelcontextprotocol/sdk/types.js"; +import { + Client, + SSEClientTransport, + StreamableHTTPClientTransport, + type CallToolResult, + type Resource, + type Tool, +} from "@modelcontextprotocol/client"; import { getTheme, onThemeChange } from "./theme"; import { HOST_STYLE_VARIABLES } from "./host-styles"; diff --git a/examples/basic-host/src/index.tsx b/examples/basic-host/src/index.tsx index 3d488a7..00c0d52 100644 --- a/examples/basic-host/src/index.tsx +++ b/examples/basic-host/src/index.tsx @@ -1,5 +1,5 @@ import { getToolUiResourceUri, McpUiToolMetaSchema } from "@modelcontextprotocol/ext-apps/app-bridge"; -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { Tool } from "@modelcontextprotocol/client"; import { Component, type ErrorInfo, type ReactNode, StrictMode, Suspense, use, useEffect, useMemo, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; import { callTool, connectToServer, hasAppHtml, initializeApp, loadSandboxProxy, log, newAppBridge, type ServerInfo, type ToolCallInfo, type ModelContext, type AppMessage } from "./implementation"; diff --git a/examples/basic-server-preact/main.ts b/examples/basic-server-preact/main.ts index 7642632..ffdff52 100644 --- a/examples/basic-server-preact/main.ts +++ b/examples/basic-server-preact/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 1a63232..0187827 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -24,10 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", diff --git a/examples/basic-server-preact/server.ts b/examples/basic-server-preact/server.ts index bf14ea5..dfec94a 100644 --- a/examples/basic-server-preact/server.ts +++ b/examples/basic-server-preact/server.ts @@ -1,8 +1,12 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js) const DIST_DIR = import.meta.filename.endsWith(".ts") ? path.join(import.meta.dirname, "dist") @@ -28,7 +32,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async (): Promise => { diff --git a/examples/basic-server-preact/src/mcp-app.tsx b/examples/basic-server-preact/src/mcp-app.tsx index a8bdc68..351a8ec 100644 --- a/examples/basic-server-preact/src/mcp-app.tsx +++ b/examples/basic-server-preact/src/mcp-app.tsx @@ -8,7 +8,7 @@ import { applyHostStyleVariables, type McpUiHostContext, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { useCallback, useEffect, useState } from "preact/hooks"; import { render } from "preact"; import styles from "./mcp-app.module.css"; diff --git a/examples/basic-server-react/main.ts b/examples/basic-server-react/main.ts index ec187b6..21c5e18 100644 --- a/examples/basic-server-react/main.ts +++ b/examples/basic-server-react/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index 821fc96..dae7665 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -34,10 +34,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/basic-server-react/server.ts b/examples/basic-server-react/server.ts index 23f6dda..d77c908 100644 --- a/examples/basic-server-react/server.ts +++ b/examples/basic-server-react/server.ts @@ -1,8 +1,12 @@ import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js) const DIST_DIR = import.meta.filename.endsWith(".ts") @@ -29,7 +33,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async (): Promise => { diff --git a/examples/basic-server-react/src/mcp-app.tsx b/examples/basic-server-react/src/mcp-app.tsx index 6bb5540..2b5fbef 100644 --- a/examples/basic-server-react/src/mcp-app.tsx +++ b/examples/basic-server-react/src/mcp-app.tsx @@ -3,7 +3,7 @@ */ import type { App, McpUiHostContext } from "@modelcontextprotocol/ext-apps"; import { useApp } from "@modelcontextprotocol/ext-apps/react"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { StrictMode, useCallback, useEffect, useState } from "react"; import { createRoot } from "react-dom/client"; import styles from "./mcp-app.module.css"; diff --git a/examples/basic-server-solid/main.ts b/examples/basic-server-solid/main.ts index c8d9de2..095ef8d 100644 --- a/examples/basic-server-solid/main.ts +++ b/examples/basic-server-solid/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index 997bc60..5d91407 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -24,10 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", diff --git a/examples/basic-server-solid/server.ts b/examples/basic-server-solid/server.ts index 2ed2d93..addb522 100644 --- a/examples/basic-server-solid/server.ts +++ b/examples/basic-server-solid/server.ts @@ -1,8 +1,12 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js) const DIST_DIR = import.meta.filename.endsWith(".ts") ? path.join(import.meta.dirname, "dist") @@ -28,7 +32,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async (): Promise => { diff --git a/examples/basic-server-solid/src/mcp-app.tsx b/examples/basic-server-solid/src/mcp-app.tsx index 66d47f3..2d78b7b 100644 --- a/examples/basic-server-solid/src/mcp-app.tsx +++ b/examples/basic-server-solid/src/mcp-app.tsx @@ -8,7 +8,7 @@ import { applyHostStyleVariables, type McpUiHostContext, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { createEffect, createSignal, onMount, Show } from "solid-js"; import { render } from "solid-js/web"; import styles from "./mcp-app.module.css"; diff --git a/examples/basic-server-svelte/main.ts b/examples/basic-server-svelte/main.ts index 6c50a25..734ec0c 100644 --- a/examples/basic-server-svelte/main.ts +++ b/examples/basic-server-svelte/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index e83772f..18bf7ab 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -24,10 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", diff --git a/examples/basic-server-svelte/server.ts b/examples/basic-server-svelte/server.ts index da603ca..9414285 100644 --- a/examples/basic-server-svelte/server.ts +++ b/examples/basic-server-svelte/server.ts @@ -1,8 +1,12 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js) const DIST_DIR = import.meta.filename.endsWith(".ts") ? path.join(import.meta.dirname, "dist") @@ -28,7 +32,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async (): Promise => { diff --git a/examples/basic-server-svelte/src/App.svelte b/examples/basic-server-svelte/src/App.svelte index fde128d..cabcc31 100644 --- a/examples/basic-server-svelte/src/App.svelte +++ b/examples/basic-server-svelte/src/App.svelte @@ -7,7 +7,7 @@ import { applyHostStyleVariables, type McpUiHostContext, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; function extractTime(result: CallToolResult): string { const { text } = result.content?.find((c) => c.type === "text")!; diff --git a/examples/basic-server-vanillajs/main.ts b/examples/basic-server-vanillajs/main.ts index 286fa34..4de7331 100644 --- a/examples/basic-server-vanillajs/main.ts +++ b/examples/basic-server-vanillajs/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 64e1758..6bb75af 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -24,10 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/basic-server-vanillajs/server.ts b/examples/basic-server-vanillajs/server.ts index 5b2daf7..693d861 100644 --- a/examples/basic-server-vanillajs/server.ts +++ b/examples/basic-server-vanillajs/server.ts @@ -1,6 +1,9 @@ import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -29,7 +32,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", - inputSchema: {}, + inputSchema: z.object({}), outputSchema: z.object({ time: z.string(), }), diff --git a/examples/basic-server-vanillajs/src/mcp-app.ts b/examples/basic-server-vanillajs/src/mcp-app.ts index c3168c2..6b5084a 100644 --- a/examples/basic-server-vanillajs/src/mcp-app.ts +++ b/examples/basic-server-vanillajs/src/mcp-app.ts @@ -8,7 +8,7 @@ import { applyHostStyleVariables, type McpUiHostContext, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import "./global.css"; import "./mcp-app.css"; diff --git a/examples/basic-server-vue/main.ts b/examples/basic-server-vue/main.ts index 669a071..d774269 100644 --- a/examples/basic-server-vue/main.ts +++ b/examples/basic-server-vue/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index e4cd0b4..3b34aa6 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -24,10 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", diff --git a/examples/basic-server-vue/server.ts b/examples/basic-server-vue/server.ts index 5aa72de..9adb911 100644 --- a/examples/basic-server-vue/server.ts +++ b/examples/basic-server-vue/server.ts @@ -1,8 +1,12 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js) const DIST_DIR = import.meta.filename.endsWith(".ts") ? path.join(import.meta.dirname, "dist") @@ -28,7 +32,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async (): Promise => { diff --git a/examples/basic-server-vue/src/App.vue b/examples/basic-server-vue/src/App.vue index 196ad64..a45c262 100644 --- a/examples/basic-server-vue/src/App.vue +++ b/examples/basic-server-vue/src/App.vue @@ -7,7 +7,7 @@ import { applyHostStyleVariables, type McpUiHostContext, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; function extractTime(result: CallToolResult): string { const { text } = result.content?.find((c) => c.type === "text")!; diff --git a/examples/budget-allocator-server/main.ts b/examples/budget-allocator-server/main.ts index cc488f0..02c03cb 100644 --- a/examples/budget-allocator-server/main.ts +++ b/examples/budget-allocator-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 8af1a6c..5d4fe8e 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -29,7 +29,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/budget-allocator-server/server.ts b/examples/budget-allocator-server/server.ts index 04fba11..01f5dbf 100755 --- a/examples/budget-allocator-server/server.ts +++ b/examples/budget-allocator-server/server.ts @@ -4,11 +4,11 @@ * Provides budget configuration, 24 months of historical allocation data, * and industry benchmarks by company stage. */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -270,7 +270,7 @@ export function createServer(): McpServer { title: "Get Budget Data", description: "Returns budget configuration with 24 months of historical allocations and industry benchmarks by company stage. The widget is interactive and exposes tools for reading/modifying allocations, adjusting budgets, and comparing against industry benchmarks.", - inputSchema: {}, + inputSchema: z.object({}), outputSchema: BudgetDataResponseSchema, _meta: { ui: { resourceUri } }, }, diff --git a/examples/cohort-heatmap-server/main.ts b/examples/cohort-heatmap-server/main.ts index d59903b..8b33603 100644 --- a/examples/cohort-heatmap-server/main.ts +++ b/examples/cohort-heatmap-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index 99e9d9f..c54040a 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -29,7 +29,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/cohort-heatmap-server/server.ts b/examples/cohort-heatmap-server/server.ts index 6d07144..129e2dc 100644 --- a/examples/cohort-heatmap-server/server.ts +++ b/examples/cohort-heatmap-server/server.ts @@ -1,5 +1,7 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -178,8 +180,8 @@ export function createServer(): McpServer { title: "Get Cohort Retention Data", description: "Returns cohort retention heatmap data showing customer retention over time by signup month", - inputSchema: GetCohortDataInputSchema.shape, - outputSchema: CohortDataSchema.shape, + inputSchema: GetCohortDataInputSchema, + outputSchema: CohortDataSchema, _meta: { ui: { resourceUri } }, }, async ({ metric, periodType, cohortCount, maxPeriods }) => { diff --git a/examples/customer-segmentation-server/main.ts b/examples/customer-segmentation-server/main.ts index 130076c..c788757 100644 --- a/examples/customer-segmentation-server/main.ts +++ b/examples/customer-segmentation-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index 59e3eb1..ef14b32 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -29,7 +29,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/customer-segmentation-server/server.ts b/examples/customer-segmentation-server/server.ts index 244415d..7cc1c1f 100644 --- a/examples/customer-segmentation-server/server.ts +++ b/examples/customer-segmentation-server/server.ts @@ -1,8 +1,8 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -100,8 +100,8 @@ export function createServer(): McpServer { title: "Get Customer Data", description: "Returns customer data with segment information for visualization. Optionally filter by segment.", - inputSchema: GetCustomerDataInputSchema.shape, - outputSchema: GetCustomerDataOutputSchema.shape, + inputSchema: GetCustomerDataInputSchema, + outputSchema: GetCustomerDataOutputSchema, _meta: { ui: { resourceUri } }, }, async ({ segment }): Promise => { diff --git a/examples/debug-server/main.ts b/examples/debug-server/main.ts index 3c89a97..9fb0825 100644 --- a/examples/debug-server/main.ts +++ b/examples/debug-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index 02e9fbf..0e3f508 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -37,7 +37,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "zod": "^4.1.13" }, "devDependencies": { diff --git a/examples/debug-server/server.ts b/examples/debug-server/server.ts index fd3452a..068f94d 100644 --- a/examples/debug-server/server.ts +++ b/examples/debug-server/server.ts @@ -3,11 +3,11 @@ import { registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import { appendFileSync } from "node:fs"; import path from "node:path"; diff --git a/examples/integration-server/main.ts b/examples/integration-server/main.ts index c45787e..9a42842 100644 --- a/examples/integration-server/main.ts +++ b/examples/integration-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json index 3f9d8bb..56fc8ed 100644 --- a/examples/integration-server/package.json +++ b/examples/integration-server/package.json @@ -15,10 +15,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/integration-server/server.ts b/examples/integration-server/server.ts index e44689d..346f4f7 100644 --- a/examples/integration-server/server.ts +++ b/examples/integration-server/server.ts @@ -3,11 +3,11 @@ import { registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -33,7 +33,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time.", - inputSchema: {}, + inputSchema: z.object({}), outputSchema: z.object({ time: z.string(), }), @@ -72,7 +72,7 @@ export function createServer(): McpServer { ); // Sample downloadable resource — used to demo ResourceLink in ui/download-file - server.resource( + server.registerResource( SAMPLE_DOWNLOAD_URI, SAMPLE_DOWNLOAD_URI, { diff --git a/examples/integration-server/src/mcp-app.tsx b/examples/integration-server/src/mcp-app.tsx index 7f0b18c..281a356 100644 --- a/examples/integration-server/src/mcp-app.tsx +++ b/examples/integration-server/src/mcp-app.tsx @@ -3,7 +3,7 @@ */ import type { App, McpUiHostContext } from "@modelcontextprotocol/ext-apps"; import { useApp } from "@modelcontextprotocol/ext-apps/react"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { StrictMode, useCallback, useEffect, useState } from "react"; import { createRoot } from "react-dom/client"; import styles from "./mcp-app.module.css"; diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index 086f20a..6aef3e1 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -24,10 +24,11 @@ "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", - "jose": "^6.0.0" + "jose": "^6.0.0", + "zod": "^4.1.13" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/lazy-auth-server/server.ts b/examples/lazy-auth-server/server.ts index fe7b1ac..01535eb 100644 --- a/examples/lazy-auth-server/server.ts +++ b/examples/lazy-auth-server/server.ts @@ -21,13 +21,13 @@ import { registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; -import { UrlElicitationRequiredError } from "@modelcontextprotocol/sdk/types.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import { + McpServer, + UrlElicitationRequiredError, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import cors from "cors"; import express, { type Express, type Request, type Response } from "express"; import { SignJWT, jwtVerify } from "jose"; @@ -35,6 +35,7 @@ import crypto from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js). Derived // from import.meta.url rather than import.meta.filename/dirname, which are @@ -605,7 +606,7 @@ export function createServer(authInfo?: AuthInfo, req?: Request): McpServer { title: "Show Auth Button", description: "Public tool: shows a button that triggers the protected get_secret tool.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri: buttonUri } }, }, async (): Promise => ({ @@ -638,7 +639,7 @@ export function createServer(authInfo?: AuthInfo, req?: Request): McpServer { title: "Get Secret", description: "Protected tool: returns secret data. Requires authentication.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri: secretUri } }, }, async (): Promise => { @@ -682,7 +683,7 @@ export function createServer(authInfo?: AuthInfo, req?: Request): McpServer { title: "Revoke Auth Token", description: "Protected tool: revokes the caller's entire auth session (access + refresh token).", - inputSchema: {}, + inputSchema: z.object({}), }, async (): Promise => { if (!authInfo) { @@ -720,7 +721,7 @@ export function createServer(authInfo?: AuthInfo, req?: Request): McpServer { title: "Elicit URL (session-callback)", description: "URL elicitation via elicitInput. Blocks until the client accepts; sends ElicitCompleteNotification before returning.", - inputSchema: {}, + inputSchema: z.object({}), }, async (_args, extra): Promise => { const eid = crypto.randomUUID(); @@ -735,7 +736,7 @@ export function createServer(authInfo?: AuthInfo, req?: Request): McpServer { message: "Please open this URL to continue.", elicitationId: eid, }, - { relatedRequestId: extra.requestId, timeout: 300_000 }, + { relatedRequestId: extra.mcpReq.id, timeout: 300_000 }, ); // Send completion notification before returning — lands on the same SSE // stream as this tool response. For Streamable HTTP this is the ONLY @@ -767,7 +768,7 @@ export function createServer(authInfo?: AuthInfo, req?: Request): McpServer { title: "Elicit by -32042 Error", description: "Throws UrlElicitationRequiredError (-32042). Client must open the URL, then retry. On retry, if the last-issued elicitationId was completed, sends ElicitCompleteNotification and succeeds.", - inputSchema: {}, + inputSchema: z.object({}), }, async (): Promise => { // Retry path: check if the user completed the elicitation we last issued. @@ -1031,7 +1032,7 @@ export function createApp(): Express { } const server = createServer(authInfo, req); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on("close", () => { diff --git a/examples/map-server/main.ts b/examples/map-server/main.ts index f91f57b..8038394 100644 --- a/examples/map-server/main.ts +++ b/examples/map-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/map-server/package.json b/examples/map-server/package.json index b0c629a..a3639ce 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -26,10 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/map-server/server.ts b/examples/map-server/server.ts index 10125a6..5767d46 100644 --- a/examples/map-server/server.ts +++ b/examples/map-server/server.ts @@ -5,11 +5,11 @@ * - geocode: Search for places using OpenStreetMap Nominatim * - show-map: Display an interactive 3D globe at a given location */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -148,7 +148,7 @@ export function createServer(): McpServer { title: "Show Map", description: "Display an interactive world map zoomed to a specific bounding box. Use the GeoCode tool to find the bounding box of a location. The widget is interactive and exposes tools for navigation (fly to locations) and querying the current view.", - inputSchema: { + inputSchema: z.object({ west: z .number() .optional() @@ -173,7 +173,7 @@ export function createServer(): McpServer { .string() .optional() .describe("Optional label to display on the map"), - }, + }), _meta: { ui: { resourceUri } }, }, async ({ west, south, east, north, label }): Promise => ({ @@ -196,13 +196,13 @@ export function createServer(): McpServer { title: "Geocode", description: "Search for places using OpenStreetMap. Returns coordinates and bounding boxes for up to 5 matches.", - inputSchema: { + inputSchema: z.object({ query: z .string() .describe( "Place name or address to search for (e.g., 'Paris', 'Golden Gate Bridge', '1600 Pennsylvania Ave')", ), - }, + }), }, async ({ query }): Promise => { try { diff --git a/examples/map-server/src/mcp-app.ts b/examples/map-server/src/mcp-app.ts index 39d7c2a..d3090c7 100644 --- a/examples/map-server/src/mcp-app.ts +++ b/examples/map-server/src/mcp-app.ts @@ -6,7 +6,7 @@ * a navigate-to tool for the host to control navigation. */ import { App } from "@modelcontextprotocol/ext-apps"; -import type { ContentBlock } from "@modelcontextprotocol/sdk/spec.types.js"; +import type { ContentBlock } from "@modelcontextprotocol/client"; import { z } from "zod"; // TypeScript declaration for Cesium loaded from CDN diff --git a/examples/pdf-server/main.ts b/examples/pdf-server/main.ts index 1ff848a..bb7b57f 100644 --- a/examples/pdf-server/main.ts +++ b/examples/pdf-server/main.ts @@ -6,10 +6,10 @@ import fs from "node:fs"; import path from "node:path"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { @@ -39,7 +39,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index aeb52c7..a7eb4bb 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -26,10 +26,11 @@ }, "dependencies": { "@cantoo/pdf-lib": "^2.6.5", + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", diff --git a/examples/pdf-server/server.test.ts b/examples/pdf-server/server.test.ts index 33a03f1..0b2a5eb 100644 --- a/examples/pdf-server/server.test.ts +++ b/examples/pdf-server/server.test.ts @@ -2,8 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, spyOn } from "bun:test"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; +import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs"; import { PDFDocument } from "@cantoo/pdf-lib"; import { makeRandomJpeg } from "../../tests/helpers/range-counting-server"; diff --git a/examples/pdf-server/server.ts b/examples/pdf-server/server.ts index 93dedfd..e67cd47 100644 --- a/examples/pdf-server/server.ts +++ b/examples/pdf-server/server.ts @@ -13,18 +13,19 @@ import { randomUUID } from "crypto"; import fs from "node:fs"; import path from "node:path"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; import { - RootsListChangedNotificationSchema, + McpServer, type CallToolResult, + type ElicitResult, + type PrimitiveSchemaDefinition, type ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; + type Server, +} from "@modelcontextprotocol/server"; // Stub DOMMatrix/ImageData/Path2D before pdfjs-dist loads — its legacy // build instantiates DOMMatrix at module scope and the @napi-rs/canvas // polyfill is unreliable under npx. See ./pdfjs-polyfill.ts for details. @@ -62,10 +63,6 @@ class FetchStandardFontDataFactory { return new Uint8Array(await res.arrayBuffer()); } } -import type { - PrimitiveSchemaDefinition, - ElicitResult, -} from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; // ============================================================================= @@ -1282,7 +1279,7 @@ export function createServer(options: CreateServerOptions = {}): McpServer { refreshRoots(server.server); }; server.server.setNotificationHandler( - RootsListChangedNotificationSchema, + "notifications/roots/list_changed", async () => { await refreshRoots(server.server); }, @@ -1292,10 +1289,12 @@ export function createServer(options: CreateServerOptions = {}): McpServer { const { readPdfRange } = sharedPdfCache; // Tool: list_pdfs - List available PDFs - server.tool( + server.registerTool( "list_pdfs", - "List available PDFs that can be displayed", - {}, + { + description: "List available PDFs that can be displayed", + inputSchema: z.object({}), + }, async (): Promise => { const seen = new Set(); const localFiles: string[] = []; @@ -1377,7 +1376,7 @@ export function createServer(options: CreateServerOptions = {}): McpServer { title: "Read PDF Bytes", description: "Read a range of bytes from a PDF (max 512KB per request). The model should NOT call this tool directly.", - inputSchema: { + inputSchema: z.object({ url: z.string().describe("PDF URL or local file path"), offset: z.number().min(0).default(0).describe("Byte offset"), byteCount: z @@ -1386,7 +1385,7 @@ export function createServer(options: CreateServerOptions = {}): McpServer { .max(MAX_CHUNK_BYTES) .default(MAX_CHUNK_BYTES) .describe("Bytes to read"), - }, + }), outputSchema: z.object({ url: z.string(), bytes: z.string().describe("Base64 encoded bytes"), @@ -1473,7 +1472,7 @@ Returns a viewUUID in structuredContent. Pass it to \`interact\`: Accepts local files (use list_pdfs), client MCP root directories, or any HTTPS URL. Set \`elicit_form_inputs\` to true to prompt the user to fill form fields before display.`, - inputSchema: { + inputSchema: z.object({ url: z .string() .default(DEFAULT_PDF) @@ -1489,7 +1488,7 @@ Set \`elicit_form_inputs\` to true to prompt the user to fill form fields before "If true and the PDF has form fields, prompt the user to fill them before displaying", ), }), - }, + }), outputSchema: z.object({ viewUUID: z .string() @@ -2426,7 +2425,7 @@ Example — add a signature image and a stamp, then screenshot to verify: **FORMS** — fill_form: fill fields with \`fields\` array of {name, value}. **SAVE** — save_as: write the annotated PDF (annotations + form values) to a file. Pass \`path\` (absolute path or file://) for a new location, or omit \`path\` to overwrite the original. Set \`overwrite: true\` to replace an existing file (always required when omitting \`path\`).`, - inputSchema: { + inputSchema: z.object({ viewUUID: z .string() .describe( @@ -2523,7 +2522,7 @@ Example — add a signature image and a stamp, then screenshot to verify: .describe( "Array of commands to execute sequentially. More efficient than separate calls. Tip: end with get_pages+getScreenshots to verify changes.", ), - }, + }), }, async ( { @@ -2600,7 +2599,7 @@ Example — add a signature image and a stamp, then screenshot to verify: const result = await processInteractCommand( uuid, commandList[i], - extra.signal, + extra.mcpReq.signal, ); if (result.isError) { const errText = result.content @@ -2657,7 +2656,7 @@ Example — add a signature image and a stamp, then screenshot to verify: title: "Submit Page Data", description: "Submit rendered page data for a get_pages request (used by viewer). The model should NOT call this tool directly.", - inputSchema: { + inputSchema: z.object({ requestId: z .string() .describe("The request ID from the get_pages command"), @@ -2670,7 +2669,7 @@ Example — add a signature image and a stamp, then screenshot to verify: }), ) .describe("Page data entries"), - }, + }), _meta: { ui: { visibility: ["app"] } }, }, async ({ requestId, pages }): Promise => { @@ -2700,7 +2699,7 @@ Example — add a signature image and a stamp, then screenshot to verify: title: "Submit Save Data", description: "Submit annotated PDF bytes for a save_as request (used by viewer). The model should NOT call this tool directly.", - inputSchema: { + inputSchema: z.object({ requestId: z .string() .describe("The request ID from the save_as command"), @@ -2709,7 +2708,7 @@ Example — add a signature image and a stamp, then screenshot to verify: .string() .optional() .describe("Error message if the viewer failed to build bytes"), - }, + }), _meta: { ui: { visibility: ["app"] } }, }, async ({ requestId, data, error }): Promise => { @@ -2739,7 +2738,7 @@ Example — add a signature image and a stamp, then screenshot to verify: title: "Submit Viewer State", description: "Submit a viewer-state snapshot for a get_viewer_state request (used by viewer). The model should NOT call this tool directly.", - inputSchema: { + inputSchema: z.object({ requestId: z .string() .describe("The request ID from the get_viewer_state command"), @@ -2751,7 +2750,7 @@ Example — add a signature image and a stamp, then screenshot to verify: .string() .optional() .describe("Error message if the viewer failed to read state"), - }, + }), _meta: { ui: { visibility: ["app"] } }, }, async ({ requestId, state, error }): Promise => { @@ -2781,9 +2780,9 @@ Example — add a signature image and a stamp, then screenshot to verify: title: "Poll PDF Commands", description: "Poll for pending commands for a PDF viewer. The model should NOT call this tool directly.", - inputSchema: { + inputSchema: z.object({ viewUUID: z.string().describe("The viewUUID of the PDF viewer"), - }, + }), _meta: { ui: { visibility: ["app"] } }, }, async ({ viewUUID: uuid }): Promise => { @@ -2828,10 +2827,10 @@ Example — add a signature image and a stamp, then screenshot to verify: title: "Save PDF", description: "Save annotated PDF bytes back to a local file. The model should NOT call this tool directly — use interact with action: save_as instead.", - inputSchema: { + inputSchema: z.object({ url: z.string().describe("Original PDF URL or local file path"), data: z.string().describe("Base64-encoded PDF bytes"), - }, + }), outputSchema: z.object({ filePath: z.string(), mtimeMs: z.number(), diff --git a/examples/pdf-server/src/mcp-app.ts b/examples/pdf-server/src/mcp-app.ts index a953e24..45e0c74 100644 --- a/examples/pdf-server/src/mcp-app.ts +++ b/examples/pdf-server/src/mcp-app.ts @@ -12,8 +12,10 @@ import { applyDocumentTheme, applyHostStyleVariables, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import type { ContentBlock } from "@modelcontextprotocol/sdk/spec.types.js"; +import type { + CallToolResult, + ContentBlock, +} from "@modelcontextprotocol/client"; import { z } from "zod"; import * as pdfjsLib from "pdfjs-dist"; import { AnnotationLayer, AnnotationMode, TextLayer } from "pdfjs-dist"; diff --git a/examples/qr-server/package.json b/examples/qr-server/package.json index a5cbf5b..cb4e010 100644 --- a/examples/qr-server/package.json +++ b/examples/qr-server/package.json @@ -8,7 +8,6 @@ "build": "echo 'No build step needed for Python server'" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/sdk": "^1.29.0" + "@modelcontextprotocol/ext-apps": "^1.7.0" } } diff --git a/examples/quickstart/main.ts b/examples/quickstart/main.ts index 06895a4..7f370a8 100644 --- a/examples/quickstart/main.ts +++ b/examples/quickstart/main.ts @@ -1,7 +1,7 @@ -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -21,7 +21,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index 1003637..f741c6e 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -18,9 +18,10 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", - "express": "^5.1.0" + "express": "^5.1.0", + "zod": "^4.1.13" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/quickstart/server.ts b/examples/quickstart/server.ts index eb3c3b1..611d0f0 100644 --- a/examples/quickstart/server.ts +++ b/examples/quickstart/server.ts @@ -3,9 +3,10 @@ import { registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { McpServer } from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; +import { z } from "zod"; const DIST_DIR = path.join(import.meta.dirname, "dist"); @@ -30,7 +31,7 @@ export function createServer(): McpServer { { title: "Get Time", description: "Returns the current server time.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, // Links this tool to its UI resource }, async () => { diff --git a/examples/say-server/package.json b/examples/say-server/package.json index a0076e6..2ebc099 100644 --- a/examples/say-server/package.json +++ b/examples/say-server/package.json @@ -15,7 +15,6 @@ "build": "echo 'No build step needed for Python server'" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/sdk": "^1.29.0" + "@modelcontextprotocol/ext-apps": "^1.7.0" } } diff --git a/examples/scenario-modeler-server/main.ts b/examples/scenario-modeler-server/main.ts index dbd3ab6..cc20e63 100644 --- a/examples/scenario-modeler-server/main.ts +++ b/examples/scenario-modeler-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index a7e8a9a..63a5d48 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -29,7 +29,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/scenario-modeler-server/server.ts b/examples/scenario-modeler-server/server.ts index b59851c..c5fa05d 100644 --- a/examples/scenario-modeler-server/server.ts +++ b/examples/scenario-modeler-server/server.ts @@ -3,11 +3,11 @@ import { registerAppResource, registerAppTool, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -305,8 +305,8 @@ export function createServer(): McpServer { title: "Get Scenario Data", description: "Returns SaaS scenario templates and optionally computes custom projections for given inputs", - inputSchema: GetScenarioDataInputSchema.shape, - outputSchema: GetScenarioDataOutputSchema.shape, + inputSchema: GetScenarioDataInputSchema, + outputSchema: GetScenarioDataOutputSchema, _meta: { ui: { resourceUri } }, }, async (args: { diff --git a/examples/shadertoy-server/main.ts b/examples/shadertoy-server/main.ts index ac8f6ad..7e388f0 100644 --- a/examples/shadertoy-server/main.ts +++ b/examples/shadertoy-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index 0c27501..318f017 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -27,7 +27,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/shadertoy-server/server.ts b/examples/shadertoy-server/server.ts index 5238c40..50c27bc 100644 --- a/examples/shadertoy-server/server.ts +++ b/examples/shadertoy-server/server.ts @@ -3,11 +3,11 @@ import { registerAppTool, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; diff --git a/examples/sheet-music-server/main.ts b/examples/sheet-music-server/main.ts index 8b143a7..d7bf3a0 100644 --- a/examples/sheet-music-server/main.ts +++ b/examples/sheet-music-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index 6cf175a..10d7c71 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -27,7 +27,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/sheet-music-server/server.ts b/examples/sheet-music-server/server.ts index cfd319b..be7214a 100644 --- a/examples/sheet-music-server/server.ts +++ b/examples/sheet-music-server/server.ts @@ -1,8 +1,8 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; diff --git a/examples/system-monitor-server/main.ts b/examples/system-monitor-server/main.ts index 365a560..c55c799 100644 --- a/examples/system-monitor-server/main.ts +++ b/examples/system-monitor-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index 760d07f..5bc4a95 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -29,7 +29,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/system-monitor-server/server.ts b/examples/system-monitor-server/server.ts index 8fac5f2..a17bcf6 100644 --- a/examples/system-monitor-server/server.ts +++ b/examples/system-monitor-server/server.ts @@ -3,11 +3,11 @@ import { registerAppResource, registerAppTool, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -135,8 +135,8 @@ export function createServer(): McpServer { title: "Get System Info", description: "Returns system information, including hostname, platform, CPU info, and memory.", - inputSchema: {}, - outputSchema: SystemInfoSchema.shape, + inputSchema: z.object({}), + outputSchema: SystemInfoSchema, _meta: { ui: { resourceUri } }, }, (): CallToolResult => { @@ -156,8 +156,8 @@ export function createServer(): McpServer { title: "Poll System Stats", description: "Returns dynamic system metrics for polling: per-core CPU timing, memory usage, and uptime. App-only.", - inputSchema: {}, - outputSchema: PollStatsSchema.shape, + inputSchema: z.object({}), + outputSchema: PollStatsSchema, _meta: { ui: { visibility: ["app"] } }, }, async (): Promise => { diff --git a/examples/threejs-server/main.ts b/examples/threejs-server/main.ts index 13a2084..ef16e26 100644 --- a/examples/threejs-server/main.ts +++ b/examples/threejs-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index 9dda288..fb97d33 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -26,10 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/threejs-server/server.ts b/examples/threejs-server/server.ts index ae320df..0a13c19 100644 --- a/examples/threejs-server/server.ts +++ b/examples/threejs-server/server.ts @@ -8,8 +8,10 @@ import { registerAppResource, registerAppTool, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -161,7 +163,7 @@ export function createServer(): McpServer { title: "Show Three.js Scene", description: "Render an interactive 3D scene with custom Three.js code. Supports transparent backgrounds (alpha: true) for seamless host UI integration. Available globals: THREE, OrbitControls, EffectComposer, RenderPass, UnrealBloomPass, canvas, width, height.", - inputSchema: { + inputSchema: z.object({ code: z .string() .default(DEFAULT_THREEJS_CODE) @@ -172,7 +174,7 @@ export function createServer(): McpServer { .positive() .default(400) .describe("Height in pixels"), - }, + }), outputSchema: z.object({ success: z.boolean(), }), @@ -192,7 +194,7 @@ export function createServer(): McpServer { { title: "Learn Three.js", description: "Get documentation and examples for using the Three.js View", - inputSchema: {}, + inputSchema: z.object({}), }, async () => { return { diff --git a/examples/threejs-server/src/mcp-app-wrapper.tsx b/examples/threejs-server/src/mcp-app-wrapper.tsx index 34194b1..44fee0c 100644 --- a/examples/threejs-server/src/mcp-app-wrapper.tsx +++ b/examples/threejs-server/src/mcp-app-wrapper.tsx @@ -6,7 +6,7 @@ */ import type { App, McpUiHostContext } from "@modelcontextprotocol/ext-apps"; import { useApp } from "@modelcontextprotocol/ext-apps/react"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { StrictMode, useState, useCallback, useEffect, useRef } from "react"; import { createRoot } from "react-dom/client"; import { z } from "zod"; diff --git a/examples/transcript-server/main.ts b/examples/transcript-server/main.ts index 68db8dc..e86ad7c 100644 --- a/examples/transcript-server/main.ts +++ b/examples/transcript-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -25,7 +25,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index ce5b737..eea4a80 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -27,7 +27,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/transcript-server/server.ts b/examples/transcript-server/server.ts index 56bf88d..863eb29 100644 --- a/examples/transcript-server/server.ts +++ b/examples/transcript-server/server.ts @@ -1,8 +1,8 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { @@ -10,6 +10,7 @@ import { registerAppResource, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; +import { z } from "zod"; // Works both from source (server.ts) and compiled (dist/server.js) const DIST_DIR = import.meta.filename.endsWith(".ts") ? path.join(import.meta.dirname, "dist") @@ -33,7 +34,7 @@ export function createServer(): McpServer { title: "Transcribe Speech", description: "Opens a live speech transcription interface using the Web Speech API.", - inputSchema: {}, + inputSchema: z.object({}), _meta: { ui: { resourceUri } }, }, async (): Promise => { diff --git a/examples/video-resource-server/main.ts b/examples/video-resource-server/main.ts index 082fa5a..1983373 100644 --- a/examples/video-resource-server/main.ts +++ b/examples/video-resource-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index 605b8de..ea816b9 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -24,10 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/video-resource-server/server.ts b/examples/video-resource-server/server.ts index 2cb317e..8ae6240 100644 --- a/examples/video-resource-server/server.ts +++ b/examples/video-resource-server/server.ts @@ -12,11 +12,9 @@ import { import { McpServer, ResourceTemplate, -} from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import fs from "node:fs/promises"; import path from "node:path"; import { z } from "zod"; @@ -135,14 +133,14 @@ Available videos: ${Object.entries(VIDEO_LIBRARY) .map(([id, v]) => `- ${id}: ${v.description}`) .join("\n")}`, - inputSchema: { + inputSchema: z.object({ videoId: z .enum(Object.keys(VIDEO_LIBRARY) as [string, ...string[]]) .default("bunny-1mb") .describe( `Video ID to play. Available: ${Object.keys(VIDEO_LIBRARY).join(", ")}`, ), - }, + }), outputSchema: z.object({ videoUri: z.string(), description: z.string(), diff --git a/examples/video-resource-server/src/mcp-app.ts b/examples/video-resource-server/src/mcp-app.ts index 88ed913..2a3f8f9 100644 --- a/examples/video-resource-server/src/mcp-app.ts +++ b/examples/video-resource-server/src/mcp-app.ts @@ -5,7 +5,7 @@ * The video is served as a base64 blob and converted to a data URI for playback. */ import { App, type McpUiHostContext } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import "./global.css"; import "./mcp-app.css"; diff --git a/examples/wiki-explorer-server/main.ts b/examples/wiki-explorer-server/main.ts index cc7b3a2..bfeeffb 100644 --- a/examples/wiki-explorer-server/main.ts +++ b/examples/wiki-explorer-server/main.ts @@ -4,10 +4,10 @@ * Or: node dist/index.js [--stdio] */ -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; +import { createMcpExpressApp } from "@modelcontextprotocol/express"; +import type { McpServer } from "@modelcontextprotocol/server"; +import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node"; import cors from "cors"; import type { Request, Response } from "express"; import { createServer } from "./server.js"; @@ -27,7 +27,7 @@ export async function startStreamableHTTPServer( app.all("/mcp", async (req: Request, res: Response) => { const server = createServer(); - const transport = new StreamableHTTPServerTransport({ + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 62f1de8..bd31d6f 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -26,10 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/wiki-explorer-server/server.ts b/examples/wiki-explorer-server/server.ts index 59d5e18..8bc4eb7 100644 --- a/examples/wiki-explorer-server/server.ts +++ b/examples/wiki-explorer-server/server.ts @@ -3,11 +3,11 @@ import { registerAppResource, registerAppTool, } from "@modelcontextprotocol/ext-apps/server"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { - CallToolResult, - ReadResourceResult, -} from "@modelcontextprotocol/sdk/types.js"; +import { + McpServer, + type CallToolResult, + type ReadResourceResult, +} from "@modelcontextprotocol/server"; import * as cheerio from "cheerio"; import fs from "node:fs/promises"; import path from "node:path"; diff --git a/examples/wiki-explorer-server/src/mcp-app.ts b/examples/wiki-explorer-server/src/mcp-app.ts index 291292d..9833d9c 100644 --- a/examples/wiki-explorer-server/src/mcp-app.ts +++ b/examples/wiki-explorer-server/src/mcp-app.ts @@ -2,7 +2,7 @@ * Wiki Explorer - Force-directed graph visualization of Wikipedia link networks */ import { App, type McpUiHostContext } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { z } from "zod"; import { forceCenter, diff --git a/package-lock.json b/package-lock.json index 5514bf1..c1ae34d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "@boneskull/typedoc-plugin-mermaid": "^0.2.0", "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", "@modelcontextprotocol/server": "2.0.0-beta.4", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", @@ -55,7 +54,6 @@ "peerDependencies": { "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", "@modelcontextprotocol/server": "2.0.0-beta.4", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -74,8 +72,8 @@ "name": "@modelcontextprotocol/ext-apps-basic-host", "version": "1.7.4", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/sdk": "^1.29.0", "react": "^19.2.0", "react-dom": "^19.2.0", "zod": "^4.1.13" @@ -100,10 +98,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", @@ -129,10 +128,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -161,10 +161,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", @@ -190,10 +191,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", @@ -219,10 +221,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -246,10 +249,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", @@ -278,7 +282,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -306,7 +310,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -338,7 +342,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -366,7 +370,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "zod": "^4.1.13" }, "bin": { @@ -388,10 +392,11 @@ "examples/integration-server": { "version": "1.7.4", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -421,10 +426,11 @@ "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", - "jose": "^6.0.0" + "jose": "^6.0.0", + "zod": "^4.1.13" }, "bin": { "mcp-server-lazy-auth": "dist/index.js" @@ -445,10 +451,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -473,10 +480,11 @@ "license": "MIT", "dependencies": { "@cantoo/pdf-lib": "^2.6.5", + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", @@ -500,8 +508,7 @@ "name": "@modelcontextprotocol/server-qr", "version": "1.7.4", "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/sdk": "^1.29.0" + "@modelcontextprotocol/ext-apps": "^1.7.0" } }, "examples/quickstart": { @@ -512,9 +519,10 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", - "express": "^5.1.0" + "express": "^5.1.0", + "zod": "^4.1.13" }, "devDependencies": { "@types/cors": "^2.8.19", @@ -533,8 +541,7 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/sdk": "^1.29.0" + "@modelcontextprotocol/ext-apps": "^1.7.0" } }, "examples/scenario-modeler-server": { @@ -545,7 +552,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -578,7 +585,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -605,7 +612,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", @@ -633,7 +640,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -659,10 +666,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -696,7 +704,7 @@ "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -721,10 +729,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -748,10 +757,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { + "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/express": "2.0.0-beta.4", "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/server": "2.0.0-beta.4", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -2230,7 +2240,6 @@ "version": "2.0.0-beta.4", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", - "dev": true, "license": "MIT", "dependencies": { "@modelcontextprotocol/core": "2.0.0-beta.4", @@ -2336,6 +2345,7 @@ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", + "peer": true, "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", @@ -3995,6 +4005,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4011,6 +4022,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -5546,6 +5558,7 @@ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", + "peer": true, "dependencies": { "ip-address": "^10.2.0" }, @@ -5563,7 +5576,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-uri": { "version": "3.1.2", @@ -5579,7 +5593,8 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/fflate": { "version": "0.8.2", @@ -5906,6 +5921,7 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -6047,6 +6063,7 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 12" } @@ -6242,13 +6259,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-schema-typed": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/json5": { "version": "2.2.3", @@ -7224,6 +7243,7 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9350,6 +9370,7 @@ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", + "peer": true, "peerDependencies": { "zod": "^3.25 || ^4" } diff --git a/package.json b/package.json index 35bcdf0..ecbf386 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,6 @@ "@boneskull/typedoc-plugin-mermaid": "^0.2.0", "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", "@modelcontextprotocol/server": "2.0.0-beta.4", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", @@ -113,7 +112,6 @@ "peerDependencies": { "@modelcontextprotocol/client": "2.0.0-beta.4", "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/sdk": "^1.29.0", "@modelcontextprotocol/server": "2.0.0-beta.4", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", diff --git a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md index 02d09a4..69e41f8 100644 --- a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md +++ b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md @@ -77,13 +77,15 @@ Before writing any code, analyze the server's existing tools and determine which ## Step 2: Add Dependencies ```bash -npm install @modelcontextprotocol/ext-apps +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 zod@^4.2.0 npm install -D vite vite-plugin-singlefile ``` Plus framework-specific dependencies if needed (e.g., `react`, `react-dom`, `@vitejs/plugin-react` for React). -Use `npm install` to add dependencies rather than manually writing version numbers. This lets npm resolve the latest compatible versions. Never specify version numbers from memory. +Use the exact base MCP SDK prerelease required by ext-apps. Existing servers +that still import `@modelcontextprotocol/sdk` v1 must migrate those imports and +handler schemas before adding the App integration. ## Step 3: Set Up the Build Pipeline @@ -146,10 +148,14 @@ Transform plain MCP tools into App tools with UI. **Before** (plain MCP tool): ```typescript -server.tool("my-tool", { param: z.string() }, async (args) => { - const data = await fetchData(args.param); - return { content: [{ type: "text", text: JSON.stringify(data) }] }; -}); +server.registerTool( + "my-tool", + { inputSchema: z.object({ param: z.string() }) }, + async ({ param }) => { + const data = await fetchData(param); + return { content: [{ type: "text", text: JSON.stringify(data) }] }; + }, +); ``` **After** (App tool with UI): @@ -158,17 +164,22 @@ import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@model const resourceUri = "ui://my-tool/mcp-app.html"; -registerAppTool(server, "my-tool", { - description: "Shows data with an interactive UI", - inputSchema: { param: z.string() }, - _meta: { ui: { resourceUri } }, -}, async (args) => { - const data = await fetchData(args.param); - return { - content: [{ type: "text", text: JSON.stringify(data) }], // text fallback for non-UI hosts - structuredContent: { data }, // structured data for the UI - }; -}); +registerAppTool( + server, + "my-tool", + { + description: "Shows data with an interactive UI", + inputSchema: z.object({ param: z.string() }), + _meta: { ui: { resourceUri } }, + }, + async ({ param }) => { + const data = await fetchData(param); + return { + content: [{ type: "text", text: JSON.stringify(data) }], // text fallback for non-UI hosts + structuredContent: { data }, // structured data for the UI + }; + }, +); ``` Key guidance: @@ -187,17 +198,21 @@ import path from "node:path"; const resourceUri = "ui://my-tool/mcp-app.html"; -registerAppResource(server, { - uri: resourceUri, - name: "My Tool UI", - mimeType: RESOURCE_MIME_TYPE, -}, async () => { - const html = await fs.readFile( - path.resolve(import.meta.dirname, "dist", "mcp-app.html"), - "utf-8", - ); - return { contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }] }; -}); +registerAppResource( + server, + "My Tool UI", + resourceUri, + {}, + async () => { + const html = await fs.readFile( + path.resolve(import.meta.dirname, "dist", "mcp-app.html"), + "utf-8", + ); + return { + contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }], + }; + }, +); ``` If multiple tools share the same UI, they can reference the same `resourceUri` and the same resource registration. @@ -271,25 +286,32 @@ registerAppTool(server, "poll-data", { }); ``` -The UI calls these via `app.callServerTool("poll-data", {})`. +The UI calls these via +`app.callServerTool({ name: "poll-data", arguments: {} })`. ### CSP Configuration If the UI needs to load external resources (fonts, APIs, CDNs), declare the domains: ```typescript -registerAppResource(server, { - uri: resourceUri, - name: "My Tool UI", - mimeType: RESOURCE_MIME_TYPE, - _meta: { - ui: { - connectDomains: ["api.example.com"], // fetch/XHR targets - resourceDomains: ["cdn.example.com"], // scripts, styles, images - frameDomains: ["embed.example.com"], // nested iframes +registerAppResource(server, "My Tool UI", resourceUri, {}, async () => ({ + contents: [ + { + uri: resourceUri, + mimeType: RESOURCE_MIME_TYPE, + text: html, + _meta: { + ui: { + csp: { + connectDomains: ["https://api.example.com"], // fetch/XHR targets + resourceDomains: ["https://cdn.example.com"], // scripts, styles, images + frameDomains: ["https://embed.example.com"], // nested iframes + }, + }, + }, }, - }, -}, async () => { /* ... */ }); + ], +})); ``` ### Streaming Partial Input @@ -326,7 +348,14 @@ server.server.oninitialized = () => { }, appToolHandler); } else { // Text-only client — register plain tool - server.tool("my-tool", "Shows data", { param: z.string() }, plainToolHandler); + server.registerTool( + "my-tool", + { + description: "Shows data", + inputSchema: z.object({ param: z.string() }), + }, + plainToolHandler, + ); } }; ``` diff --git a/plugins/mcp-apps/skills/convert-web-app/SKILL.md b/plugins/mcp-apps/skills/convert-web-app/SKILL.md index 5db2d94..22686ef 100644 --- a/plugins/mcp-apps/skills/convert-web-app/SKILL.md +++ b/plugins/mcp-apps/skills/convert-web-app/SKILL.md @@ -115,19 +115,20 @@ Create a new MCP server with tool and resource registration. This wraps the exis ### Dependencies ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 zod@^4.2.0 npm install -D tsx vite vite-plugin-singlefile ``` -Use `npm install` to add dependencies rather than manually writing version numbers. This lets npm resolve the latest compatible versions. Never specify version numbers from memory. +Use `npm install` with the exact base MCP SDK prerelease required by ext-apps. +Do not substitute unpublished local packages or guess a different prerelease. ### Server Code Create `server.ts`: ```typescript -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { McpServer } from "@modelcontextprotocol/server"; +import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server"; import fs from "node:fs/promises"; import path from "node:path"; @@ -140,7 +141,9 @@ const resourceUri = "ui://my-app/mcp-app.html"; // Register the tool — inputSchema maps to the app's data sources registerAppTool(server, "show-app", { description: "Displays the app with the given parameters", - inputSchema: { query: z.string().describe("The search query") }, + inputSchema: z.object({ + query: z.string().describe("The search query"), + }), _meta: { ui: { resourceUri } }, }, async (args) => { // Process args server-side if needed @@ -151,19 +154,29 @@ registerAppTool(server, "show-app", { }); // Register the HTML resource -registerAppResource(server, { - uri: resourceUri, - name: "My App UI", - mimeType: RESOURCE_MIME_TYPE, - // Add CSP domains from Step 2 if needed: - // _meta: { ui: { connectDomains: ["api.example.com"], resourceDomains: ["cdn.example.com"] } }, -}, async () => { - const html = await fs.readFile( - path.resolve(import.meta.dirname, "dist", "mcp-app.html"), - "utf-8", - ); - return { contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }] }; -}); +registerAppResource( + server, + "My App UI", + resourceUri, + {}, + async () => { + const html = await fs.readFile( + path.resolve(import.meta.dirname, "dist", "mcp-app.html"), + "utf-8", + ); + return { + contents: [ + { + uri: resourceUri, + mimeType: RESOURCE_MIME_TYPE, + text: html, + // Add CSP domains from Step 2 if needed: + // _meta: { ui: { csp: { connectDomains: ["https://api.example.com"], resourceDomains: ["https://cdn.example.com"] } } }, + }, + ], + }; + }, +); // Start the server const transport = new StdioServerTransport(); @@ -310,7 +323,10 @@ const data = await fetch("/api/data").then(r => r.json()); // After (hybrid): async function fetchData(): Promise { if (isMcpApp) { - const result = await app.callServerTool("fetch-data", {}); + const result = await app.callServerTool({ + name: "fetch-data", + arguments: {}, + }); return result.structuredContent; } return fetch("/api/data").then(r => r.json()); @@ -437,7 +453,8 @@ registerAppTool(server, "refresh-data", { }); ``` -The UI calls these via `app.callServerTool("refresh-data", {})`. +The UI calls these via +`app.callServerTool({ name: "refresh-data", arguments: {} })`. ### Streaming Partial Input diff --git a/plugins/mcp-apps/skills/create-mcp-app/SKILL.md b/plugins/mcp-apps/skills/create-mcp-app/SKILL.md index 5bd1219..fc41e70 100644 --- a/plugins/mcp-apps/skills/create-mcp-app/SKILL.md +++ b/plugins/mcp-apps/skills/create-mcp-app/SKILL.md @@ -110,11 +110,12 @@ See `/tmp/mcp-ext-apps/docs/patterns.md` for detailed recipes: **Always** use `npm install` to add dependencies rather than manually writing version numbers: ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod express cors +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 @modelcontextprotocol/node@2.0.0-beta.4 @modelcontextprotocol/express@2.0.0-beta.4 zod@^4.2.0 express cors npm install -D typescript vite vite-plugin-singlefile concurrently cross-env @types/node @types/express @types/cors ``` -This lets npm resolve the latest compatible versions. **Never** specify version numbers from memory. +Use the exact base MCP SDK prerelease required by ext-apps. Do not substitute +unpublished local packages or guess a different prerelease. ### TypeScript Server Execution diff --git a/scripts/typedoc-plugin-base-sdk-links.mjs b/scripts/typedoc-plugin-base-sdk-links.mjs new file mode 100644 index 0000000..6098918 --- /dev/null +++ b/scripts/typedoc-plugin-base-sdk-links.mjs @@ -0,0 +1,16 @@ +/** + * Resolve module-local links inherited from the base MCP SDK declarations. + * + * TypeDoc's externalSymbolLinkMappings handles package-qualified symbols, but + * the base SDK writes this particular reference as `index.inputRequired`. + */ + +const BASE_SDK_DOCS = "https://ts.sdk.modelcontextprotocol.io/v2/"; + +/** @param {import("typedoc").Application} app */ +export function load(app) { + app.converter.addUnknownSymbolResolver((ref) => { + const name = ref.symbolReference?.path?.map((part) => part.path).join("."); + if (name === "index.inputRequired") return BASE_SDK_DOCS; + }); +} diff --git a/src/app-bridge.examples.ts b/src/app-bridge.examples.ts index d5dc5bc..47ad623 100644 --- a/src/app-bridge.examples.ts +++ b/src/app-bridge.examples.ts @@ -7,17 +7,11 @@ * @module */ -import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; -import { - CallToolResult, - CallToolResultSchema, +import { Client, type Transport } from "@modelcontextprotocol/client"; +import type { CreateMessageRequest, CreateMessageResult, - ListResourcesResultSchema, - ReadResourceResultSchema, - ListPromptsResultSchema, -} from "@modelcontextprotocol/sdk/types.js"; +} from "@modelcontextprotocol/server"; import { AppBridge, PostMessageTransport } from "./app-bridge.js"; import type { McpUiDisplayMode } from "./types.js"; @@ -223,8 +217,7 @@ function AppBridge_oncalltool_forwardToServer( bridge.oncalltool = async (params, extra) => { return mcpClient.request( { method: "tools/call", params }, - CallToolResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; //#endregion AppBridge_oncalltool_forwardToServer @@ -245,7 +238,9 @@ function AppBridge_oncreatesamplingmessage_forwardToLlm( //#region AppBridge_oncreatesamplingmessage_forwardToLlm bridge.oncreatesamplingmessage = async (params, extra) => { // Apply rate limiting, user approval, cost controls here - return await myLlmProvider.complete(params, { signal: extra.signal }); + return await myLlmProvider.complete(params, { + signal: extra.mcpReq.signal, + }); }; //#endregion AppBridge_oncreatesamplingmessage_forwardToLlm } @@ -261,8 +256,7 @@ function AppBridge_onlistresources_returnResources( bridge.onlistresources = async (params, extra) => { return mcpClient.request( { method: "resources/list", params }, - ListResourcesResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; //#endregion AppBridge_onlistresources_returnResources @@ -279,8 +273,7 @@ function AppBridge_onreadresource_returnResource( bridge.onreadresource = async (params, extra) => { return mcpClient.request( { method: "resources/read", params }, - ReadResourceResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; //#endregion AppBridge_onreadresource_returnResource @@ -297,8 +290,7 @@ function AppBridge_onlistprompts_returnPrompts( bridge.onlistprompts = async (params, extra) => { return mcpClient.request( { method: "prompts/list", params }, - ListPromptsResultSchema, - { signal: extra.signal }, + { signal: extra.mcpReq.signal }, ); }; //#endregion AppBridge_onlistprompts_returnPrompts @@ -444,10 +436,10 @@ async function AppBridge_sendToolResult_afterExecution( args: Record, ) { //#region AppBridge_sendToolResult_afterExecution - const result = await mcpClient.request( - { method: "tools/call", params: { name: "get_weather", arguments: args } }, - CallToolResultSchema, - ); + const result = await mcpClient.request({ + method: "tools/call", + params: { name: "get_weather", arguments: args }, + }); bridge.sendToolResult(result); //#endregion AppBridge_sendToolResult_afterExecution } diff --git a/src/app-bridge.ts b/src/app-bridge.ts index c65510d..d353cf7 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -261,7 +261,7 @@ export type AppBridgeEventMap = { /** * Host-side bridge for communicating with a single View ({@link app!App `App`}). * - * `AppBridge` extends the MCP SDK's `Protocol` class and acts as a proxy between + * `AppBridge` extends the base MCP SDK's public `Server` class and acts as a proxy between * the host application and a view running in an iframe. When an MCP client * is provided to the constructor, it automatically forwards MCP server capabilities * (tools, resources, prompts) to the view. It also handles the initialization @@ -667,8 +667,7 @@ export class AppBridge extends Server { * ```typescript * bridge.onsandboxready = async () => { * const resource = await mcpClient.request( - * { method: "resources/read", params: { uri: "ui://my-app" } }, - * ReadResourceResultSchema + * { method: "resources/read", params: { uri: "ui://my-app" } } * ); * * bridge.sendSandboxResourceReady({ @@ -1154,14 +1153,13 @@ export class AppBridge extends Server { * bridge.oncalltool = async (params, extra) => { * return mcpClient.request( * { method: "tools/call", params }, - * CallToolResultSchema, - * { signal: extra.signal }, + * { signal: extra.mcpReq.signal }, * ); * }; * ``` * - * @see `CallToolRequest` from @modelcontextprotocol/sdk for the request type - * @see `CallToolResult` from @modelcontextprotocol/sdk for the result type + * @see `CallToolRequest` from @modelcontextprotocol/server for the request type + * @see `CallToolResult` from @modelcontextprotocol/server for the result type */ private _oncalltool?: ( params: CallToolRequest["params"], @@ -1209,12 +1207,14 @@ export class AppBridge extends Server { * ```ts source="./app-bridge.examples.ts#AppBridge_oncreatesamplingmessage_forwardToLlm" * bridge.oncreatesamplingmessage = async (params, extra) => { * // Apply rate limiting, user approval, cost controls here - * return await myLlmProvider.complete(params, { signal: extra.signal }); + * return await myLlmProvider.complete(params, { + * signal: extra.mcpReq.signal, + * }); * }; * ``` * - * @see `CreateMessageRequest` from @modelcontextprotocol/sdk for the request type - * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/sdk for result types + * @see `CreateMessageRequest` from @modelcontextprotocol/server for the request type + * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/server for result types */ set oncreatesamplingmessage( callback: ( @@ -1244,7 +1244,7 @@ export class AppBridge extends Server { * }); * ``` * - * @see `ToolListChangedNotification` from @modelcontextprotocol/sdk for the notification type + * @see `ToolListChangedNotification` from @modelcontextprotocol/server for the notification type */ sendToolListChanged(params: ToolListChangedNotification["params"] = {}) { return this.notification({ @@ -1270,14 +1270,13 @@ export class AppBridge extends Server { * bridge.onlistresources = async (params, extra) => { * return mcpClient.request( * { method: "resources/list", params }, - * ListResourcesResultSchema, - * { signal: extra.signal }, + * { signal: extra.mcpReq.signal }, * ); * }; * ``` * - * @see `ListResourcesRequest` from @modelcontextprotocol/sdk for the request type - * @see `ListResourcesResult` from @modelcontextprotocol/sdk for the result type + * @see `ListResourcesRequest` from @modelcontextprotocol/server for the request type + * @see `ListResourcesResult` from @modelcontextprotocol/server for the result type */ private _onlistresources?: ( params: ListResourcesRequest["params"], @@ -1325,14 +1324,13 @@ export class AppBridge extends Server { * bridge.onlistresourcetemplates = async (params, extra) => { * return mcpClient.request( * { method: "resources/templates/list", params }, - * ListResourceTemplatesResultSchema, - * { signal: extra.signal } + * { signal: extra.mcpReq.signal } * ); * }; * ``` * - * @see `ListResourceTemplatesRequest` from @modelcontextprotocol/sdk for the request type - * @see `ListResourceTemplatesResult` from @modelcontextprotocol/sdk for the result type + * @see `ListResourceTemplatesRequest` from @modelcontextprotocol/server for the request type + * @see `ListResourceTemplatesResult` from @modelcontextprotocol/server for the result type */ private _onlistresourcetemplates?: ( params: ListResourceTemplatesRequest["params"], @@ -1383,14 +1381,13 @@ export class AppBridge extends Server { * bridge.onreadresource = async (params, extra) => { * return mcpClient.request( * { method: "resources/read", params }, - * ReadResourceResultSchema, - * { signal: extra.signal }, + * { signal: extra.mcpReq.signal }, * ); * }; * ``` * - * @see `ReadResourceRequest` from @modelcontextprotocol/sdk for the request type - * @see `ReadResourceResult` from @modelcontextprotocol/sdk for the result type + * @see `ReadResourceRequest` from @modelcontextprotocol/server for the request type + * @see `ReadResourceResult` from @modelcontextprotocol/server for the result type */ private _onreadresource?: ( params: ReadResourceRequest["params"], @@ -1438,7 +1435,7 @@ export class AppBridge extends Server { * }); * ``` * - * @see `ResourceListChangedNotification` from @modelcontextprotocol/sdk for the notification type + * @see `ResourceListChangedNotification` from @modelcontextprotocol/server for the notification type */ sendResourceListChanged( params: ResourceListChangedNotification["params"] = {}, @@ -1466,14 +1463,13 @@ export class AppBridge extends Server { * bridge.onlistprompts = async (params, extra) => { * return mcpClient.request( * { method: "prompts/list", params }, - * ListPromptsResultSchema, - * { signal: extra.signal }, + * { signal: extra.mcpReq.signal }, * ); * }; * ``` * - * @see `ListPromptsRequest` from @modelcontextprotocol/sdk for the request type - * @see `ListPromptsResult` from @modelcontextprotocol/sdk for the result type + * @see `ListPromptsRequest` from @modelcontextprotocol/server for the request type + * @see `ListPromptsResult` from @modelcontextprotocol/server for the result type */ private _onlistprompts?: ( params: ListPromptsRequest["params"], @@ -1520,7 +1516,7 @@ export class AppBridge extends Server { * }); * ``` * - * @see `PromptListChangedNotification` from @modelcontextprotocol/sdk for the notification type + * @see `PromptListChangedNotification` from @modelcontextprotocol/server for the notification type */ sendPromptListChanged(params: PromptListChangedNotification["params"] = {}) { return this.notification({ @@ -1722,10 +1718,10 @@ export class AppBridge extends Server { * * @example * ```ts source="./app-bridge.examples.ts#AppBridge_sendToolResult_afterExecution" - * const result = await mcpClient.request( - * { method: "tools/call", params: { name: "get_weather", arguments: args } }, - * CallToolResultSchema, - * ); + * const result = await mcpClient.request({ + * method: "tools/call", + * params: { name: "get_weather", arguments: args }, + * }); * bridge.sendToolResult(result); * ``` * diff --git a/src/app.examples.ts b/src/app.examples.ts index 81bdd44..7d475ef 100644 --- a/src/app.examples.ts +++ b/src/app.examples.ts @@ -7,11 +7,8 @@ * @module */ -import type { Tool } from "@modelcontextprotocol/sdk/types.js"; -import type { - McpServer, - ToolCallback, -} from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { SamplingMessage, Tool } from "@modelcontextprotocol/client"; +import type { McpServer, ToolCallback } from "@modelcontextprotocol/server"; import { App, PostMessageTransport, @@ -369,7 +366,7 @@ async function App_createSamplingMessage_simple(app: App) { */ async function App_createSamplingMessage_withTools( app: App, - messages: import("@modelcontextprotocol/sdk/types.js").SamplingMessage[], + messages: SamplingMessage[], ) { //#region App_createSamplingMessage_withTools if (!app.getHostCapabilities()?.sampling?.tools) return; diff --git a/src/app.ts b/src/app.ts index 3c8f5b7..7e752ce 100644 --- a/src/app.ts +++ b/src/app.ts @@ -174,9 +174,9 @@ export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; /** * Options for configuring {@link App `App`} behavior. * - * Extends `ProtocolOptions` from the MCP SDK with `App`-specific configuration. + * Extends `ClientOptions` from the base MCP SDK with `App`-specific configuration. * - * @see `ProtocolOptions` from @modelcontextprotocol/sdk for inherited options + * @see `ClientOptions` from @modelcontextprotocol/client for inherited options */ export type AppOptions = Omit< ClientOptions, @@ -243,7 +243,7 @@ export type AppToolResult< * validated/parsed input; when `In` is `undefined`, the callback receives only * `extra`. When `Out` is provided, the return's `structuredContent` is typed. * - * Mirrors `ToolCallback` from `@modelcontextprotocol/sdk/server/mcp.js` but is + * Mirrors `ToolCallback` from `@modelcontextprotocol/server` but is * parameterized over {@link StandardSchemaV1} instead of zod, so any * Standard-Schema-compatible library (Zod, ArkType, Valibot, …) can be used. */ @@ -261,7 +261,7 @@ export type AppToolCallback< /** * Handle returned by {@link App.registerTool}. Mirrors `RegisteredTool` from - * `@modelcontextprotocol/sdk/server/mcp.js` but stores + * `@modelcontextprotocol/server` but stores * {@link StandardSchemaV1} schemas. */ export type RegisteredAppTool = { @@ -302,9 +302,8 @@ export type AppEventMap = { * Main class for MCP Apps to communicate with their host. * * The `App` class provides a framework-agnostic way to build interactive MCP Apps - * that run inside host applications. It extends the MCP SDK's `Protocol` class and - * handles the connection lifecycle, initialization handshake, and bidirectional - * communication with the host. + * that run inside host applications. It extends the base MCP SDK's public + * `Client` class and adds the Apps handshake, lifecycle, and high-level APIs. * * ## Architecture * @@ -321,7 +320,7 @@ export type AppEventMap = { * * ## Protocol and event methods * - * As a subclass of the MCP SDK's `Protocol`, `App` provides: + * As a subclass of the base MCP SDK's `Client`, `App` provides: * - `setRequestHandler()` - Register handlers for requests from host * - `setNotificationHandler()` - Register handlers for notifications from host * - `addEventListener()` - Append a listener for a notification event (multi-listener) @@ -1458,8 +1457,8 @@ export class App extends Client { * } * ``` * - * @see `CreateMessageRequest` from @modelcontextprotocol/sdk for the request type - * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/sdk for result types + * @see `CreateMessageRequest` from @modelcontextprotocol/client for the request type + * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/client for result types */ async createSamplingMessage( params: CreateMessageRequest["params"] & { tools?: undefined }, diff --git a/src/events.ts b/src/events.ts index b891867..aff1446 100644 --- a/src/events.ts +++ b/src/events.ts @@ -38,9 +38,8 @@ export class EventDispatcher> { getHandler( event: K, ): ((params: EventMap[K]) => void) | undefined { - return (this._eventSlots.get(event) as - | EventSlot - | undefined)?.onHandler; + return (this._eventSlots.get(event) as EventSlot | undefined) + ?.onHandler; } /** Add a listener. Listeners run in insertion order after the handler. */ diff --git a/src/message-transport.test.ts b/src/message-transport.test.ts index d5f5092..2a115f3 100644 --- a/src/message-transport.test.ts +++ b/src/message-transport.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"; -import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js"; +import type { JSONRPCMessage } from "@modelcontextprotocol/client"; import { PostMessageTransport } from "./message-transport"; diff --git a/src/server/index.examples.ts b/src/server/index.examples.ts index d583ae8..9ceebce 100644 --- a/src/server/index.examples.ts +++ b/src/server/index.examples.ts @@ -9,17 +9,14 @@ import * as crypto from "node:crypto"; import * as fs from "node:fs/promises"; -import type { - McpServer, - ToolCallback, - ReadResourceCallback, -} from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer, ToolCallback } from "@modelcontextprotocol/server"; import { z } from "zod"; import { registerAppTool, registerAppResource, getUiCapability, RESOURCE_MIME_TYPE, + type ReadResourceCallback, } from "./index.js"; // Stubs for external functions used in examples @@ -74,7 +71,7 @@ function registerAppTool_basicUsage(server: McpServer) { { title: "Get Weather", description: "Get current weather for a location", - inputSchema: { location: z.string() }, + inputSchema: z.object({ location: z.string() }), _meta: { ui: { resourceUri: "ui://weather/view.html" }, }, @@ -122,7 +119,7 @@ function registerAppTool_appOnlyVisibility(server: McpServer) { "update-quantity", { description: "Update item quantity in cart", - inputSchema: { itemId: z.string(), quantity: z.number() }, + inputSchema: z.object({ itemId: z.string(), quantity: z.number() }), _meta: { ui: { resourceUri: "ui://shop/cart.html", diff --git a/src/server/index.test.ts b/src/server/index.test.ts index 1853cc0..5de341f 100644 --- a/src/server/index.test.ts +++ b/src/server/index.test.ts @@ -7,7 +7,7 @@ import { getUiCapability, EXTENSION_ID, } from "./index"; -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer } from "@modelcontextprotocol/server"; describe("registerAppTool", () => { it("should pass through config to server.registerTool", () => { diff --git a/src/server/index.ts b/src/server/index.ts index b8b3ad0..61e7438 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -150,7 +150,7 @@ export interface McpUiAppResourceConfig extends ResourceMetadata { * { * title: "Get Weather", * description: "Get current weather for a location", - * inputSchema: { location: z.string() }, + * inputSchema: z.object({ location: z.string() }), * _meta: { * ui: { resourceUri: "ui://weather/view.html" }, * }, @@ -190,7 +190,7 @@ export interface McpUiAppResourceConfig extends ResourceMetadata { * "update-quantity", * { * description: "Update item quantity in cart", - * inputSchema: { itemId: z.string(), quantity: z.number() }, + * inputSchema: z.object({ itemId: z.string(), quantity: z.number() }), * _meta: { * ui: { * resourceUri: "ui://shop/cart.html", diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 00babc4..041bf5e 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -1,5 +1,7 @@ import { OptionDefaults } from "typedoc"; +const BASE_SDK_DOCS = "https://ts.sdk.modelcontextprotocol.io/v2/"; + /** @type {Partial} */ const config = { name: "MCP Apps", @@ -29,6 +31,28 @@ const config = { excludePrivate: true, excludeInternal: false, intentionallyNotExported: ["RequestHandlerExtra"], + externalSymbolLinkMappings: { + "@modelcontextprotocol/client": { + ClientOptions: BASE_SDK_DOCS, + ConnectOptions: BASE_SDK_DOCS, + DiscoverResult: BASE_SDK_DOCS, + ProtocolError: BASE_SDK_DOCS, + "Protocol.close": BASE_SDK_DOCS, + "Protocol.notification": BASE_SDK_DOCS, + ResponseCacheStore: BASE_SDK_DOCS, + SdkError: BASE_SDK_DOCS, + "SdkErrorCode.MethodNotSupportedByProtocolVersion": BASE_SDK_DOCS, + "__type.enforceStrictCapabilities": BASE_SDK_DOCS, + }, + "@modelcontextprotocol/server": { + LoggingMessageNotification: BASE_SDK_DOCS, + "McpServer.registerTool": BASE_SDK_DOCS, + "MessageExtraInfo.classification": BASE_SDK_DOCS, + "Protocol.close": BASE_SDK_DOCS, + "Protocol.notification": BASE_SDK_DOCS, + "SdkErrorCode.UnsupportedResultType": BASE_SDK_DOCS, + }, + }, blockTags: [...OptionDefaults.blockTags, "@description"], jsDocCompatibility: { exampleTag: false, @@ -49,6 +73,7 @@ const config = { out: "docs/api", plugin: [ "typedoc-github-theme", + "./scripts/typedoc-plugin-base-sdk-links.mjs", "./scripts/typedoc-plugin-fix-mermaid-entities.mjs", "./scripts/typedoc-plugin-seo.mjs", "./scripts/typedoc-plugin-mcpstyle.mjs", From f50ec6ebfbca14bf3d3070b6d01b90f12ba93359 Mon Sep 17 00:00:00 2001 From: Andrew Khadder Date: Tue, 14 Jul 2026 17:16:27 -0700 Subject: [PATCH 12/45] fix: regenerate lockfile for npm 10 --- package-lock.json | 401 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) diff --git a/package-lock.json b/package-lock.json index c1ae34d..9e0a71b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -93,6 +93,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-host/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-host/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-preact": { "name": "@modelcontextprotocol/server-basic-preact", "version": "1.7.4", @@ -123,6 +140,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-preact/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-preact/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-react": { "name": "@modelcontextprotocol/server-basic-react", "version": "1.7.4", @@ -156,6 +190,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-react/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-react/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-solid": { "name": "@modelcontextprotocol/server-basic-solid", "version": "1.7.4", @@ -186,6 +237,23 @@ "vite-plugin-solid": "^2.11.12" } }, + "examples/basic-server-solid/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-solid/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-svelte": { "name": "@modelcontextprotocol/server-basic-svelte", "version": "1.7.4", @@ -216,6 +284,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-svelte/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-svelte/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-vanillajs": { "name": "@modelcontextprotocol/server-basic-vanillajs", "version": "1.7.4", @@ -244,6 +329,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-vanillajs/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-vanillajs/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-vue": { "name": "@modelcontextprotocol/server-basic-vue", "version": "1.7.4", @@ -274,6 +376,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-vue/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-vue/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/budget-allocator-server": { "name": "@modelcontextprotocol/server-budget-allocator", "version": "1.7.4", @@ -302,6 +421,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/budget-allocator-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/budget-allocator-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/cohort-heatmap-server": { "name": "@modelcontextprotocol/server-cohort-heatmap", "version": "1.7.4", @@ -334,6 +470,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/cohort-heatmap-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/cohort-heatmap-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/customer-segmentation-server": { "name": "@modelcontextprotocol/server-customer-segmentation", "version": "1.7.4", @@ -362,6 +515,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/customer-segmentation-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/customer-segmentation-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/debug-server": { "name": "@modelcontextprotocol/server-debug", "version": "1.7.4", @@ -389,6 +559,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/debug-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/debug-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/integration-server": { "version": "1.7.4", "dependencies": { @@ -419,6 +606,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/integration-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/integration-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/lazy-auth-server": { "name": "@modelcontextprotocol/server-lazy-auth", "version": "1.7.4", @@ -446,6 +650,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/lazy-auth-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/lazy-auth-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/map-server": { "name": "@modelcontextprotocol/server-map", "version": "1.7.4", @@ -474,6 +695,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/map-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/map-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/pdf-server": { "name": "@modelcontextprotocol/server-pdf", "version": "1.7.4", @@ -504,6 +742,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/pdf-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/pdf-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", "version": "1.7.4", @@ -536,6 +791,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/quickstart/node_modules/@types/node": { + "version": "22.19.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.5.tgz", + "integrity": "sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", "version": "1.7.4", @@ -577,6 +842,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/scenario-modeler-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/scenario-modeler-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/shadertoy-server": { "name": "@modelcontextprotocol/server-shadertoy", "version": "1.7.4", @@ -604,6 +886,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/shadertoy-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/shadertoy-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/sheet-music-server": { "name": "@modelcontextprotocol/server-sheet-music", "version": "1.7.4", @@ -632,6 +931,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/sheet-music-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/sheet-music-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/system-monitor-server": { "name": "@modelcontextprotocol/server-system-monitor", "version": "1.7.4", @@ -661,6 +977,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/system-monitor-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/system-monitor-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/threejs-server": { "name": "@modelcontextprotocol/server-threejs", "version": "1.7.4", @@ -696,6 +1029,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/threejs-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/threejs-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/transcript-server": { "name": "@modelcontextprotocol/server-transcript", "version": "1.7.4", @@ -724,6 +1074,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/transcript-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/transcript-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/video-resource-server": { "name": "@modelcontextprotocol/server-video-resource", "version": "1.7.4", @@ -752,6 +1119,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/video-resource-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/video-resource-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/wiki-explorer-server": { "name": "@modelcontextprotocol/server-wiki-explorer", "version": "1.7.4", @@ -782,6 +1166,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/wiki-explorer-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/wiki-explorer-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", From 9778cf97a2643e58d43d3e5cdc86fe6a43773fbc Mon Sep 17 00:00:00 2001 From: Mel Ludowise Date: Thu, 16 Jul 2026 09:00:17 -0700 Subject: [PATCH 13/45] Make lazy-auth-server usable on mobile (#714) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The consent page shipped without a viewport meta tag, so mobile browsers laid it out at a desktop width. Its Approve/Deny buttons were 37px tall and nested a - -`); + + + +`); return; } diff --git a/examples/lazy-auth-server/src/mcp-app.css b/examples/lazy-auth-server/src/mcp-app.css index ba4f323..800b224 100644 --- a/examples/lazy-auth-server/src/mcp-app.css +++ b/examples/lazy-auth-server/src/mcp-app.css @@ -17,9 +17,17 @@ display: flex; flex-wrap: wrap; gap: var(--spacing-sm); + /* Keep a long press on or near the buttons from starting text selection + or raising the iOS callout menu instead of a click. */ + -webkit-user-select: none; + user-select: none; + -webkit-touch-callout: none; } button { + /* Match the consent page's 56px tap targets (server.ts .btn). */ + min-height: 56px; + min-width: 56px; padding: var(--spacing-sm) var(--spacing-md); border: none; border-radius: var(--border-radius-md); @@ -27,12 +35,20 @@ button { font-weight: var(--font-weight-bold); background-color: var(--color-accent); cursor: pointer; + /* Suppress the double-tap-to-zoom gesture so a tap resolves to a click + immediately instead of waiting to see if a second tap follows. */ + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; &:disabled { opacity: 0.5; cursor: not-allowed; } + &:active:not(:disabled) { + filter: brightness(0.85); + } + &:hover:not(:disabled) { background-color: color-mix( in srgb, From 2ca6a59d2f493b227a83a2e3ce0396db4705621a Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Thu, 16 Jul 2026 20:15:10 -0400 Subject: [PATCH 14/45] spec: sync object-src with default-src (#715) --- specification/draft/apps.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 96c8286..369ef64 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -289,6 +289,7 @@ When `_meta.ui` is present on **both**, the content-item value takes precedence. style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self' data:; + object-src 'none'; connect-src 'none'; ``` From ccae093d1ab04b182e17817a3e457f7b6b1c8aea Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Wed, 15 Jul 2026 08:30:45 +0000 Subject: [PATCH 15/45] Port App and AppBridge to the public v2 Protocol base class Replaces the Client/Server subclassing from the v2 migration with direct Protocol extension, now that the SDK exports Protocol from the client package root. This restores the v1 wire contract on the iframe channel: ui/initialize is the first and only handshake, so deployed hosts keep working unchanged. Deleted with the double handshake: the inner MCP initialize round-trip and its pinned protocol version, the contentless inner capabilities and their retro-registration plumbing, the oninitialized defineProperty shadow, and the getCapabilities/getHostCapabilities split (v1 getCapabilities restored). Tests pin the wire order and assert no MCP initialize is sent in either direction. Note: package.json overrides point the SDK packages at locally built tarballs carrying the Protocol export; replace with published versions before any upstream PR. --- AGENTS.md | 8 +-- docs/overview.md | 2 +- package-lock.json | 16 ++--- package.json | 11 +-- src/app-bridge.test.ts | 10 +-- src/app-bridge.ts | 150 +++++++++++++++++------------------------ src/app.test.ts | 22 ++---- src/app.ts | 52 +++++--------- 8 files changed, 105 insertions(+), 166 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f850aa3..4b0faa0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,8 +60,8 @@ rm -fR package-lock.json node_modules && \ ### Key Source Files -- `src/app.ts` - `App` subclasses the base MCP SDK `Client`, handles View initialization, tool calls, and messaging -- `src/app-bridge.ts` - `AppBridge` subclasses the base MCP SDK `Server` for the inner iframe channel and proxies through a separate outer `Client` +- `src/app.ts` - `App` subclasses the base MCP SDK `Protocol`, handles View initialization, tool calls, and messaging +- `src/app-bridge.ts` - `AppBridge` subclasses the base MCP SDK `Protocol` for the iframe channel and proxies through a separate outer `Client` - `src/server/index.ts` - Helpers for MCP servers to register tools/resources with UI metadata - `src/types.ts` - Protocol types re-exported from `spec.types.ts` and Zod schemas from `generated/schema.ts` (auto-generated during build) - `src/message-transport.ts` - `PostMessageTransport` for iframe communication @@ -70,12 +70,12 @@ rm -fR package-lock.json node_modules && \ ### Protocol Flow ``` -View (App/Client) <--PostMessageTransport--> Host (AppBridge/Server) <--separate MCP Client--> MCP Server +View (App/Protocol) <--PostMessageTransport--> Host (AppBridge/Protocol) <--separate MCP Client--> MCP Server ``` 1. Host creates iframe with view HTML 2. View creates `App` instance and calls `connect()` with `PostMessageTransport` -3. View and Host complete the temporary inner base MCP handshake, then the View sends `ui/initialize` and receives authoritative Apps capabilities and context +3. View sends `ui/initialize` (the only handshake on the iframe channel) and receives authoritative Apps capabilities and context 4. Host sends `sendToolInput()` with tool arguments after initialization 5. View can call server tools via `app.callServerTool()` or send messages via `app.sendMessage()` 6. Host sends `sendToolResult()` when tool execution completes diff --git a/docs/overview.md b/docs/overview.md index 317ab43..63156ec 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -97,7 +97,7 @@ sequenceDiagram ``` 1. **Discovery** — The Host learns about tools and their UI resources when connecting to the server. -2. **Initialization** — When a UI tool is called, the Host renders the iframe. The SDK first completes its temporary inner base MCP handshake, then the View sends `ui/initialize` and receives host context (theme, capabilities, container dimensions). `ui/initialize` remains authoritative for Apps capabilities and identity, and `ui/notifications/initialized` remains the public View-ready signal. +2. **Initialization** — When a UI tool is called, the Host renders the iframe. The View sends `ui/initialize` (the only handshake on the iframe channel) and receives host context (theme, capabilities, container dimensions). `ui/initialize` is authoritative for Apps capabilities and identity, and `ui/notifications/initialized` is the public View-ready signal. 3. **Data delivery** — The Host sends tool arguments and, once available, tool results to the View. Results include both `content` (text for the model's context) and optionally `structuredContent` (data optimized for UI rendering). This separation lets servers provide rich data to the UI without bloating the model's context. 4. **Interactive phase** — The user interacts with the View. The View can call tools, send messages, or update context. 5. **Teardown** — Before unmounting, the Host notifies the View so it can save state or release resources. diff --git a/package-lock.json b/package-lock.json index 9e0a71b..04b0d2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ }, "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/client": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-client.tgz", + "@modelcontextprotocol/core": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-core.tgz", + "@modelcontextprotocol/server": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-server.tgz", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -2639,8 +2639,8 @@ }, "node_modules/@modelcontextprotocol/client": { "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.4.tgz", - "integrity": "sha512-VNHA/UXDk7mCpVl+jOg5B4WMRRD2OEl+it360Lhi6HiCbrIB2f6pZ0cqSDKXQVUtZvqnhdQHzZAM9h8EKWhq/A==", + "resolved": "file:../tarballs/mcp-client.tgz", + "integrity": "sha512-aCTBtOjOSOj+SOLsoUfDjhD3Ngs4pMXzAczBmKRTesiz0N/3wUEXEmKgxhfbhF6Wtgk4tatWBUG9LDaj919K2Q==", "license": "MIT", "dependencies": { "@modelcontextprotocol/core": "2.0.0-beta.4", @@ -2657,7 +2657,7 @@ }, "node_modules/@modelcontextprotocol/core": { "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.4.tgz", + "resolved": "file:../tarballs/mcp-core.tgz", "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", "license": "MIT", "dependencies": { @@ -2784,8 +2784,8 @@ }, "node_modules/@modelcontextprotocol/server": { "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.4.tgz", - "integrity": "sha512-pjMZcNEt1dOq0aJCcY3b7w1Ayh+qmQN2xlfTELcGV9yiAjEGIczBPBLWWW0k0zzo5T8kiv15jtKPsWeHGxLvLg==", + "resolved": "file:../tarballs/mcp-server.tgz", + "integrity": "sha512-/UJubByzGV+mlU8fN8yyuMI9zgoq4Ic5b1V/6yhzxrKjmXpTbNkY5rD2jRsWBk+At0AVcXLkcDzmLF91oOJtKw==", "license": "MIT", "dependencies": { "@modelcontextprotocol/core": "2.0.0-beta.4", diff --git a/package.json b/package.json index ecbf386..59e1802 100644 --- a/package.json +++ b/package.json @@ -77,9 +77,9 @@ "author": "Olivier Chafik", "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/client": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-client.tgz", + "@modelcontextprotocol/core": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-core.tgz", + "@modelcontextprotocol/server": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-server.tgz", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -129,7 +129,10 @@ "seroval": "1.4.1", "seroval-plugins": "1.4.2", "solid-js": "1.9.10", - "@types/node": "20.19.27" + "@types/node": "20.19.27", + "@modelcontextprotocol/core": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-core.tgz", + "@modelcontextprotocol/client": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-client.tgz", + "@modelcontextprotocol/server": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-server.tgz" }, "dependencies": { "@standard-schema/spec": "^1.1.0" diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index 81e072f..a80a961 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -82,7 +82,7 @@ describe("App <-> AppBridge integration", () => { expect(initializedFired).toBe(true); }); - it("keeps standard initialization separate from the Apps-ready gate", async () => { + it("sends only the Apps handshake on the wire and gates the Apps-ready callback", async () => { const methods: string[] = []; let releaseAppsInitialized!: () => void; let reachedAppsInitialized!: () => void; @@ -115,14 +115,9 @@ describe("App <-> AppBridge integration", () => { await reachedGate; expect(methods).toEqual([ - "initialize", - "notifications/initialized", "ui/initialize", "ui/notifications/initialized", ]); - expect(app.getNegotiatedProtocolVersion()).toBe("2025-11-25"); - expect(bridge.getNegotiatedProtocolVersion()).toBe("2025-11-25"); - expect(bridge.getClientVersion()).toEqual(testAppInfo); expect(bridge.getAppVersion()).toEqual(testAppInfo); expect(singularCalls).toBe(0); expect(listenerCalls).toBe(0); @@ -143,8 +138,7 @@ describe("App <-> AppBridge integration", () => { const hostCaps = app.getHostCapabilities(); expect(hostCaps).toEqual(testHostCapabilities); - expect(bridge.getHostCapabilities()).toEqual(testHostCapabilities); - expect(bridge.getCapabilities()).toEqual({}); + expect(bridge.getCapabilities()).toEqual(testHostCapabilities); }); it("Bridge receives app info and capabilities after initialization", async () => { diff --git a/src/app-bridge.ts b/src/app-bridge.ts index d353cf7..467c33d 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -1,6 +1,8 @@ -import { type Client } from "@modelcontextprotocol/client"; import { - Server, + Protocol, + type BaseContext, + type Client, + type ProtocolOptions, type CallToolRequest, type CallToolResult, type CreateMessageRequest, @@ -24,13 +26,10 @@ import { type RequestOptions, type ResourceListChangedNotification, type Result, - type ServerCapabilities, - type ServerContext, - type ServerOptions, type Tool, type ToolListChangedNotification, type Transport, -} from "@modelcontextprotocol/server"; +} from "@modelcontextprotocol/client"; import { EmptyResultSchema, LoggingMessageNotificationSchema, @@ -43,8 +42,6 @@ type MethodSchema = ZodObject<{ params: ZodType; }>; -const INNER_MCP_PROTOCOL_VERSION = "2025-11-25"; - import { type McpUiSandboxResourceReadyNotification, type McpUiSizeChangedNotification, @@ -96,20 +93,6 @@ export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./app"; import { RESOURCE_URI_META_KEY } from "./app"; export { PostMessageTransport } from "./message-transport"; -function proxiedInnerCapabilities( - capabilities: ServerCapabilities, -): ServerCapabilities { - return { - ...(capabilities.tools && { tools: capabilities.tools }), - ...(capabilities.resources && { - resources: { - listChanged: capabilities.resources.listChanged, - }, - }), - ...(capabilities.prompts && { prompts: capabilities.prompts }), - }; -} - /** * Extract UI resource URI from tool metadata. * @@ -215,13 +198,10 @@ export function buildAllowAttribute( * * @property hostContext - Optional initial host context to provide to the view * - * @see `ServerOptions` from @modelcontextprotocol/server for available options + * @see `ProtocolOptions` from @modelcontextprotocol/client for available options * @see {@link McpUiHostContext `McpUiHostContext`} for the hostContext structure */ -export type HostOptions = Omit< - ServerOptions, - "capabilities" | "supportedProtocolVersions" -> & { +export type HostOptions = Omit & { hostContext?: McpUiHostContext; }; @@ -241,7 +221,7 @@ export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION]; * * @internal */ -type RequestHandlerExtra = ServerContext; +type RequestHandlerExtra = BaseContext; /** * Maps DOM-style event names to their notification `params` types. @@ -315,7 +295,7 @@ export type AppBridgeEventMap = { * await bridge.connect(transport); * ``` */ -export class AppBridge extends Server { +export class AppBridge extends Protocol { private _appCapabilities?: McpUiAppCapabilities; private _hostContext: McpUiHostContext = {}; private _appInfo?: Implementation; @@ -328,26 +308,17 @@ export class AppBridge extends Server { * handler registration. AppBridge uses it to preserve the existing warning * when a View calls host methods before `ui/notifications/initialized`, * without adding overload-forwarding adapters around `setRequestHandler`. - * Chaining through `super._wrapHandler()` retains the base `Server`'s own - * role-specific behavior, including tool-result validation. The two - * initialization methods and `ping` are exempt because they are valid before - * the Apps-ready gate. This wraps registered callbacks, not JSON-RPC dispatch. + * `ui/initialize` and `ping` are exempt because they are valid before the + * Apps-ready gate. This wraps registered callbacks, not JSON-RPC dispatch. * * @see {@link https://github.com/anthropics/claude-ai-mcp/issues/149 claude-ai-mcp#149} */ protected override _wrapHandler( method: string, - handler: ( - request: JSONRPCRequest, - context: ServerContext, - ) => Promise, - ): (request: JSONRPCRequest, context: ServerContext) => Promise { + handler: (request: JSONRPCRequest, context: BaseContext) => Promise, + ): (request: JSONRPCRequest, context: BaseContext) => Promise { const wrapped = super._wrapHandler(method, handler); - if ( - method === "initialize" || - method === "ui/initialize" || - method === "ping" - ) { + if (method === "ui/initialize" || method === "ping") { return wrapped; } return async (request, context) => { @@ -439,14 +410,6 @@ export class AppBridge extends Server { } } - private ensureInnerCapabilities(capabilities: ServerCapabilities): void { - const current = super.getCapabilities(); - const missing = Object.keys(capabilities).some( - (key) => current[key as keyof ServerCapabilities] === undefined, - ); - if (missing) this.registerCapabilities(capabilities); - } - /** * Create a new AppBridge instance. * @@ -486,30 +449,7 @@ export class AppBridge extends Server { private _hostCapabilities: McpUiHostCapabilities, options?: HostOptions, ) { - // TEMPORARY INNER-CHANNEL NEGOTIATION: - // App and AppBridge use the public v2 Client/Server roles, so the iframe - // channel first completes the SDK's standard legacy MCP initialization. - // ui/initialize remains authoritative for Apps capabilities, identity, and - // host context, and ui/notifications/initialized remains the View-ready gate. - // Keep the inner MCP era pinned to 2025-11-25. Revisit this sequence only - // after the MCP Apps negotiation specification is resolved: - // https://github.com/modelcontextprotocol/ext-apps/issues/708 - super(_hostInfo, { - ...options, - capabilities: {}, - supportedProtocolVersions: [INNER_MCP_PROTOCOL_VERSION], - }); - - // The base Server's standard initialized callback is MCP-ready state. The - // public AppBridge callback remains exclusively the Apps View-ready gate. - super.setNotificationHandler("notifications/initialized", () => {}); - Object.defineProperty(this, "oninitialized", { - configurable: true, - enumerable: true, - get: () => this.getEventHandler("initialized"), - set: (callback: AppBridge["oninitialized"]) => - this.setEventHandler("initialized", callback), - }); + super(options); this._ensureEventSlot("initialized"); @@ -544,6 +484,35 @@ export class AppBridge extends Server { ); } + /** @internal */ + protected buildContext(ctx: BaseContext): BaseContext { + return ctx; + } + + /** + * Verify that the view supports the capability required for the given request method. + * @internal + */ + protected assertCapabilityForMethod(_method: string): void { + // Host-to-view requests are not capability-gated. + } + + /** + * Verify that a request handler is registered and supported for the given method. + * @internal + */ + protected assertRequestHandlerCapability(_method: string): void { + // Host handler registration is not capability-gated. + } + + /** + * Verify that the host supports the capability required for the given notification method. + * @internal + */ + protected assertNotificationCapability(_method: string): void { + // Host notifications are not capability-gated. + } + /** * Get the view's capabilities discovered during initialization. * @@ -713,9 +682,18 @@ export class AppBridge extends Server { * @see {@link sendToolInput `sendToolInput`} for sending tool arguments to the View * @deprecated Use {@link addEventListener `addEventListener("initialized", handler)`} instead — it composes with other listeners and supports cleanup via {@link removeEventListener `removeEventListener`}. */ - override oninitialized?: ( - params?: McpUiInitializedNotification["params"], - ) => void; + get oninitialized(): + | ((params?: McpUiInitializedNotification["params"]) => void) + | undefined { + return this.getEventHandler("initialized"); + } + set oninitialized( + callback: + | ((params?: McpUiInitializedNotification["params"]) => void) + | undefined, + ) { + this.setEventHandler("initialized", callback); + } /** * Register a handler for message requests from the view. @@ -1178,7 +1156,6 @@ export class AppBridge extends Server { ) { this.warnIfRequestHandlerReplaced("oncalltool", this._oncalltool, callback); this._oncalltool = callback; - this.ensureInnerCapabilities({ tools: {} }); this.setRequestHandler("tools/call", async (request, extra) => { if (!this._oncalltool) throw new Error("No oncalltool handler set"); return this._oncalltool(request.params, extra); @@ -1299,7 +1276,6 @@ export class AppBridge extends Server { callback, ); this._onlistresources = callback; - this.ensureInnerCapabilities({ resources: {} }); this.setRequestHandler("resources/list", async (request, extra) => { if (!this._onlistresources) throw new Error("No onlistresources handler set"); @@ -1353,7 +1329,6 @@ export class AppBridge extends Server { callback, ); this._onlistresourcetemplates = callback; - this.ensureInnerCapabilities({ resources: {} }); this.setRequestHandler( "resources/templates/list", async (request, extra) => { @@ -1410,7 +1385,6 @@ export class AppBridge extends Server { callback, ); this._onreadresource = callback; - this.ensureInnerCapabilities({ resources: {} }); this.setRequestHandler("resources/read", async (request, extra) => { if (!this._onreadresource) throw new Error("No onreadresource handler set"); @@ -1492,7 +1466,6 @@ export class AppBridge extends Server { callback, ); this._onlistprompts = callback; - this.ensureInnerCapabilities({ prompts: {} }); this.setRequestHandler("prompts/list", async (request, extra) => { if (!this._onlistprompts) throw new Error("No onlistprompts handler set"); return this._onlistprompts(request.params, extra); @@ -1532,7 +1505,7 @@ export class AppBridge extends Server { * * @see {@link McpUiHostCapabilities `McpUiHostCapabilities`} for the capabilities structure */ - getHostCapabilities(): McpUiHostCapabilities { + getCapabilities(): McpUiHostCapabilities { return this._hostCapabilities; } @@ -1565,7 +1538,7 @@ export class AppBridge extends Server { return { protocolVersion, - hostCapabilities: this.getHostCapabilities(), + hostCapabilities: this.getCapabilities(), hostInfo: this._hostInfo, hostContext: this._hostContext, }; @@ -1931,7 +1904,6 @@ export class AppBridge extends Server { if (!serverCapabilities) { throw new Error("Client server capabilities not available"); } - this.registerCapabilities(proxiedInnerCapabilities(serverCapabilities)); if (serverCapabilities.tools) { this.oncalltool = async (params, extra) => { @@ -1989,9 +1961,9 @@ export class AppBridge extends Server { } } - // The standard MCP handshake only establishes the temporary inner Server - // role. ui/notifications/initialized remains the public View-ready signal. - + // Protocol.connect only wires the transport — no MCP handshake happens on + // the iframe channel. The View drives ui/initialize; + // ui/notifications/initialized remains the public View-ready signal. return super.connect(transport); } } diff --git a/src/app.test.ts b/src/app.test.ts index dcf2d52..9057710 100644 --- a/src/app.test.ts +++ b/src/app.test.ts @@ -14,8 +14,6 @@ import { McpUiInitializedNotificationSchema, } from "./types"; -const INNER_MCP_PROTOCOL_VERSION = "2025-11-25"; - type ConnectedPair = { app: App; server: Server; @@ -34,7 +32,6 @@ async function connectPair( { name: "inner-mcp-server", version: "0.0.0" }, { capabilities: { resources: {}, tools: {} }, - supportedProtocolVersions: [INNER_MCP_PROTOCOL_VERSION], }, ); const [appTransport, serverTransport] = InMemoryTransport.createLinkedPair(); @@ -103,8 +100,8 @@ afterEach(async () => { ); }); -describe("App base MCP SDK v2 Client migration", () => { - it("completes the legacy MCP handshake before the authoritative Apps handshake", async () => { +describe("App base MCP SDK v2 Protocol migration", () => { + it("performs only the Apps handshake — no MCP initialize on the wire", async () => { const pair = await connectPair(); connected.push(pair); @@ -123,25 +120,19 @@ describe("App base MCP SDK v2 Client migration", () => { ].includes(method), ); + // The filter above includes the core MCP lifecycle methods, so this + // equality also proves no `initialize`/`notifications/initialized` was sent + // — the wire contract deployed v1 hosts depend on. expect(lifecycleMethods).toEqual([ - "initialize", - "notifications/initialized", "ui/initialize", "ui/notifications/initialized", ]); - expect(pair.app.getNegotiatedProtocolVersion()).toBe( - INNER_MCP_PROTOCOL_VERSION, - ); }); it("uses ui/initialize as the authority for Apps host state", async () => { const pair = await connectPair(); connected.push(pair); - expect(pair.app.getServerVersion()).toEqual({ - name: "inner-mcp-server", - version: "0.0.0", - }); expect(pair.app.getHostVersion()).toEqual({ name: "apps-host", version: "2.0.0", @@ -219,9 +210,6 @@ describe("App base MCP SDK v2 Client migration", () => { const second = await connectPair(first.app); connected.push(second); - expect(second.app.getNegotiatedProtocolVersion()).toBe( - INNER_MCP_PROTOCOL_VERSION, - ); expect(second.app.getHostVersion()).toEqual({ name: "apps-host", version: "2.0.0", diff --git a/src/app.ts b/src/app.ts index 7e752ce..c639784 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,7 @@ import { - Client, - type ClientContext, - type ClientOptions, + Protocol, + type BaseContext, + type ProtocolOptions, type RequestOptions, type CallToolRequest, type CallToolResult, @@ -71,8 +71,6 @@ type MethodSchema = ZodObject<{ params: ZodType; }>; -const INNER_MCP_PROTOCOL_VERSION = "2025-11-25"; - function mergeAppCapabilities( base: McpUiAppCapabilities, additional: McpUiAppCapabilities, @@ -174,14 +172,11 @@ export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; /** * Options for configuring {@link App `App`} behavior. * - * Extends `ClientOptions` from the base MCP SDK with `App`-specific configuration. + * Extends `ProtocolOptions` from the base MCP SDK with `App`-specific configuration. * - * @see `ClientOptions` from @modelcontextprotocol/client for inherited options + * @see `ProtocolOptions` from @modelcontextprotocol/client for inherited options */ -export type AppOptions = Omit< - ClientOptions, - "capabilities" | "supportedProtocolVersions" | "versionNegotiation" -> & { +export type AppOptions = Omit & { /** * Automatically report size changes to the host using `ResizeObserver`. * @@ -221,7 +216,7 @@ export type AppOptions = Omit< allowUnsafeEval?: boolean; }; -type RequestHandlerExtra = ClientContext; +type RequestHandlerExtra = BaseContext; /** * Result of an app-registered tool callback. When `Out` is provided, @@ -354,7 +349,7 @@ export type AppEventMap = { * await app.connect(); * ``` */ -export class App extends Client { +export class App extends Protocol { private _hostCapabilities?: McpUiHostCapabilities; private _hostInfo?: Implementation; private _hostContext?: McpUiHostContext; @@ -521,20 +516,7 @@ export class App extends Client { private _appCapabilities: McpUiAppCapabilities = {}, private options: AppOptions = { autoResize: true }, ) { - // TEMPORARY INNER-CHANNEL NEGOTIATION: - // App and AppBridge use the public v2 Client/Server roles, so the iframe - // channel first completes the SDK's standard legacy MCP initialization. - // ui/initialize remains authoritative for Apps capabilities, identity, and - // host context, and ui/notifications/initialized remains the View-ready gate. - // Keep the inner MCP era pinned to 2025-11-25. Revisit this sequence only - // after the MCP Apps negotiation specification is resolved: - // https://github.com/modelcontextprotocol/ext-apps/issues/708 - super(_appInfo, { - ...options, - capabilities: {}, - versionNegotiation: { mode: "legacy" }, - supportedProtocolVersions: [INNER_MCP_PROTOCOL_VERSION], - }); + super(options); if (!options.allowUnsafeEval) { z.config({ jitless: true }); @@ -550,6 +532,11 @@ export class App extends Client { this.setEventHandler("hostcontextchanged", undefined); } + /** @internal */ + protected buildContext(ctx: BaseContext): BaseContext { + return ctx; + } + private registerAppCapabilities(capabilities: McpUiAppCapabilities): void { if (this.transport) { throw new Error( @@ -1992,15 +1979,10 @@ export class App extends Client { } this._initializedSent = false; - // TEMPORARY INNER-CHANNEL NEGOTIATION: - // App and AppBridge use the public v2 Client/Server roles, so the iframe - // channel first completes the SDK's standard legacy MCP initialization. - // ui/initialize remains authoritative for Apps capabilities, identity, and - // host context, and ui/notifications/initialized remains the View-ready gate. - // Keep the inner MCP era pinned to 2025-11-25. Revisit this sequence only - // after the MCP Apps negotiation specification is resolved: - // https://github.com/modelcontextprotocol/ext-apps/issues/708 try { + // Protocol.connect only wires the transport — no MCP handshake. The + // ui/initialize request below is the sole negotiation on this channel, + // matching the v1 wire behavior deployed hosts expect. await super.connect(transport); const result = await this.request( From 43eed45404d085b63815acd971d7ab097817ec99 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Wed, 15 Jul 2026 08:34:35 +0000 Subject: [PATCH 16/45] Point SDK packages at the pkg.pr.new preview of typescript-sdk#2501 The Protocol-based migration needs the Protocol class exported from the package roots, which lands in modelcontextprotocol/typescript-sdk#2501. Pin the preview builds so the branch is installable; swap to the next published beta once that PR is released. --- package-lock.json | 20 ++++++++++---------- package.json | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04b0d2e..e82271b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ }, "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-client.tgz", - "@modelcontextprotocol/core": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-core.tgz", - "@modelcontextprotocol/server": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-server.tgz", + "@modelcontextprotocol/client": "https://pkg.pr.new/@modelcontextprotocol/client@2501", + "@modelcontextprotocol/core": "https://pkg.pr.new/@modelcontextprotocol/core@2501", + "@modelcontextprotocol/server": "https://pkg.pr.new/@modelcontextprotocol/server@2501", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -2639,11 +2639,11 @@ }, "node_modules/@modelcontextprotocol/client": { "version": "2.0.0-beta.4", - "resolved": "file:../tarballs/mcp-client.tgz", - "integrity": "sha512-aCTBtOjOSOj+SOLsoUfDjhD3Ngs4pMXzAczBmKRTesiz0N/3wUEXEmKgxhfbhF6Wtgk4tatWBUG9LDaj919K2Q==", + "resolved": "https://pkg.pr.new/@modelcontextprotocol/client@2501", + "integrity": "sha512-5FvmSf2qWlJXlI9ydf8230f1rsDHMEw9Jgcq2ZUGmA3DJShaYV6t49zKJBuawetMcLOzWBGs8+qDzhZOZTk6Fw==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/core": "https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/core@f60b401", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", @@ -2657,7 +2657,7 @@ }, "node_modules/@modelcontextprotocol/core": { "version": "2.0.0-beta.4", - "resolved": "file:../tarballs/mcp-core.tgz", + "resolved": "https://pkg.pr.new/@modelcontextprotocol/core@2501", "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", "license": "MIT", "dependencies": { @@ -2784,11 +2784,11 @@ }, "node_modules/@modelcontextprotocol/server": { "version": "2.0.0-beta.4", - "resolved": "file:../tarballs/mcp-server.tgz", - "integrity": "sha512-/UJubByzGV+mlU8fN8yyuMI9zgoq4Ic5b1V/6yhzxrKjmXpTbNkY5rD2jRsWBk+At0AVcXLkcDzmLF91oOJtKw==", + "resolved": "https://pkg.pr.new/@modelcontextprotocol/server@2501", + "integrity": "sha512-mI+V52bTdSCHDrsrVG3TeOzgy+apDQXz6l1B4KuwKV/8xbRAsV9LZ2Nf67SEoLszuYYH0as4pZQUG4cCMYP5/g==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/core": "2.0.0-beta.4", + "@modelcontextprotocol/core": "https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/core@f60b401", "zod": "^4.2.0" }, "engines": { diff --git a/package.json b/package.json index 59e1802..e95e0be 100644 --- a/package.json +++ b/package.json @@ -77,9 +77,9 @@ "author": "Olivier Chafik", "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-client.tgz", - "@modelcontextprotocol/core": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-core.tgz", - "@modelcontextprotocol/server": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-server.tgz", + "@modelcontextprotocol/client": "https://pkg.pr.new/@modelcontextprotocol/client@2501", + "@modelcontextprotocol/core": "https://pkg.pr.new/@modelcontextprotocol/core@2501", + "@modelcontextprotocol/server": "https://pkg.pr.new/@modelcontextprotocol/server@2501", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -130,9 +130,9 @@ "seroval-plugins": "1.4.2", "solid-js": "1.9.10", "@types/node": "20.19.27", - "@modelcontextprotocol/core": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-core.tgz", - "@modelcontextprotocol/client": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-client.tgz", - "@modelcontextprotocol/server": "file:/root/src/mcp/projects/v2-2026-ext-apps/tarballs/mcp-server.tgz" + "@modelcontextprotocol/core": "https://pkg.pr.new/@modelcontextprotocol/core@2501", + "@modelcontextprotocol/client": "https://pkg.pr.new/@modelcontextprotocol/client@2501", + "@modelcontextprotocol/server": "https://pkg.pr.new/@modelcontextprotocol/server@2501" }, "dependencies": { "@standard-schema/spec": "^1.1.0" From 7664bb1010aa364e84c76f8260bf1701d38b0a10 Mon Sep 17 00:00:00 2001 From: Enrico <2827496+tonxxd@users.noreply.github.com> Date: Mon, 20 Jul 2026 11:29:43 -0700 Subject: [PATCH 17/45] test: isolate optional SDK role dependencies --- build.bun.ts | 5 +- package-lock.json | 6 + package.json | 7 ++ scripts/check-dependency-isolation.mjs | 159 +++++++++++++++++++++++++ src/app-bridge.ts | 4 +- src/app.ts | 3 +- src/constants.ts | 5 + src/core-types.ts | 20 ++++ src/server/index.ts | 5 +- src/spec.types.ts | 2 +- typedoc.config.mjs | 2 + 11 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 scripts/check-dependency-isolation.mjs create mode 100644 src/constants.ts create mode 100644 src/core-types.ts diff --git a/build.bun.ts b/build.bun.ts index 837279f..61361e6 100644 --- a/build.bun.ts +++ b/build.bun.ts @@ -1,6 +1,9 @@ #!/usr/bin/env bun import { $ } from "bun"; -import { cpSync, mkdirSync } from "node:fs"; +import { cpSync, mkdirSync, rmSync } from "node:fs"; + +// Avoid publishing artifacts left behind by an earlier build or branch. +rmSync("dist", { recursive: true, force: true }); // Run TypeScript compiler for type declarations await $`tsc`; diff --git a/package-lock.json b/package-lock.json index e82271b..c7d677e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,12 @@ "zod": "^4.2.0" }, "peerDependenciesMeta": { + "@modelcontextprotocol/client": { + "optional": true + }, + "@modelcontextprotocol/server": { + "optional": true + }, "react": { "optional": true }, diff --git a/package.json b/package.json index e95e0be..4f88483 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "prepack": "npm run build", "build:all": "npm run examples:build", "test": "bun test src examples", + "test:dependency-isolation": "npm run build && node scripts/check-dependency-isolation.mjs", "test:e2e": "playwright test", "test:e2e:update": "playwright test --update-snapshots", "test:e2e:ui": "playwright test --ui", @@ -118,6 +119,12 @@ "zod": "^4.2.0" }, "peerDependenciesMeta": { + "@modelcontextprotocol/client": { + "optional": true + }, + "@modelcontextprotocol/server": { + "optional": true + }, "react": { "optional": true }, diff --git a/scripts/check-dependency-isolation.mjs b/scripts/check-dependency-isolation.mjs new file mode 100644 index 0000000..2ec28ce --- /dev/null +++ b/scripts/check-dependency-isolation.mjs @@ -0,0 +1,159 @@ +import { execFileSync } from "node:child_process"; +import { + mkdtempSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; + +const root = resolve(import.meta.dirname, ".."); +const packageJson = JSON.parse( + readFileSync(join(root, "package.json"), "utf8"), +); +const client = "@modelcontextprotocol/client"; +const server = "@modelcontextprotocol/server"; + +for (const role of [client, server]) { + if (!packageJson.peerDependencies?.[role]) { + throw new Error(`${role} must remain a peer dependency`); + } + if (packageJson.peerDependenciesMeta?.[role]?.optional !== true) { + throw new Error(`${role} must be an optional peer dependency`); + } +} + +const temporaryRoot = mkdtempSync(join(tmpdir(), "ext-apps-role-peers-")); +try { + const npmEnvironment = { + ...process.env, + npm_config_cache: join(temporaryRoot, "npm-cache"), + }; + const packOutput = JSON.parse( + execFileSync( + "npm", + [ + "pack", + "--ignore-scripts", + "--json", + "--pack-destination", + temporaryRoot, + ], + { cwd: root, encoding: "utf8", env: npmEnvironment }, + ), + ); + const tarball = join(temporaryRoot, packOutput[0].filename); + + const consumers = [ + { + name: "app-only", + dependencies: { + "@types/node": packageJson.devDependencies["@types/node"], + [client]: packageJson.devDependencies[client], + "@modelcontextprotocol/ext-apps": `file:${tarball}`, + }, + absent: server, + entry: + 'import { App } from "@modelcontextprotocol/ext-apps"; import { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; console.log(App, AppBridge);', + }, + { + name: "server-only", + dependencies: { + "@types/node": packageJson.devDependencies["@types/node"], + "@modelcontextprotocol/ext-apps": `file:${tarball}`, + [server]: packageJson.devDependencies[server], + }, + absent: client, + entry: + 'import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; console.log(registerAppTool);', + }, + ]; + + for (const consumer of consumers) { + const directory = join(temporaryRoot, consumer.name); + mkdirSync(directory); + writeFileSync( + join(directory, "package.json"), + JSON.stringify({ + private: true, + type: "module", + dependencies: consumer.dependencies, + }), + ); + execFileSync( + "npm", + [ + "install", + "--ignore-scripts", + "--package-lock=false", + "--no-audit", + "--no-fund", + ], + { cwd: directory, stdio: "pipe", env: npmEnvironment }, + ); + + const absentPath = join( + directory, + "node_modules", + ...consumer.absent.split("/"), + "package.json", + ); + try { + readFileSync(absentPath); + throw new Error( + `${consumer.name} unexpectedly installed ${consumer.absent}`, + ); + } catch (error) { + if (error?.code !== "ENOENT") throw error; + } + + writeFileSync(join(directory, "entry.ts"), consumer.entry); + writeFileSync( + join(directory, "tsconfig.json"), + JSON.stringify({ + compilerOptions: { + lib: ["ES2020", "DOM"], + module: "ESNext", + moduleResolution: "bundler", + noEmit: true, + skipLibCheck: false, + strict: true, + target: "ES2020", + }, + include: ["entry.ts"], + }), + ); + execFileSync( + process.execPath, + [join(root, "node_modules", "typescript", "bin", "tsc")], + { cwd: directory, stdio: "inherit" }, + ); + + const metafile = join(directory, "bundle-meta.json"); + execFileSync( + join(root, "node_modules", ".bin", "esbuild"), + [ + "entry.ts", + "--bundle", + "--platform=browser", + "--outfile=bundle.js", + `--metafile=${metafile}`, + ], + { cwd: directory, stdio: "pipe" }, + ); + const bundleInputs = Object.keys( + JSON.parse(readFileSync(metafile, "utf8")).inputs, + ).join("\n"); + if (bundleInputs.includes(`/node_modules/${consumer.absent}/`)) { + throw new Error( + `${consumer.name} unexpectedly bundled ${consumer.absent}`, + ); + } + } +} finally { + rmSync(temporaryRoot, { recursive: true, force: true }); +} + +console.log("Role peer dependency isolation checks passed."); diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 467c33d..d6a2368 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -89,8 +89,8 @@ import { McpUiToolMeta, } from "./types"; export * from "./types"; -export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./app"; -import { RESOURCE_URI_META_KEY } from "./app"; +export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./constants"; +import { RESOURCE_URI_META_KEY } from "./constants"; export { PostMessageTransport } from "./message-transport"; /** diff --git a/src/app.ts b/src/app.ts index c639784..0ff369e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -22,6 +22,7 @@ import { type Transport, } from "@modelcontextprotocol/client"; import { EmptyResultSchema } from "@modelcontextprotocol/core"; +export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants"; import { EventDispatcher } from "./events"; export { EventDispatcher } from "./events"; import { PostMessageTransport } from "./message-transport"; @@ -158,7 +159,6 @@ export { * } * ``` */ -export const RESOURCE_URI_META_KEY = "ui/resourceUri"; /** * MIME type for MCP UI resources. @@ -167,7 +167,6 @@ export const RESOURCE_URI_META_KEY = "ui/resourceUri"; * * Used by {@link server-helpers!registerAppResource `registerAppResource`} as the default MIME type for app resources. */ -export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; /** * Options for configuring {@link App `App`} behavior. diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..f69cfa5 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,5 @@ +/** Legacy metadata key associating an MCP tool with its App resource. */ +export const RESOURCE_URI_META_KEY = "ui/resourceUri"; + +/** MIME type for MCP App HTML resources. */ +export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; diff --git a/src/core-types.ts b/src/core-types.ts new file mode 100644 index 0000000..19392d2 --- /dev/null +++ b/src/core-types.ts @@ -0,0 +1,20 @@ +import { + CallToolResultSchema, + ContentBlockSchema, + EmbeddedResourceSchema, + ImplementationSchema, + RequestIdSchema, + ResourceLinkSchema, + ToolSchema, +} from "@modelcontextprotocol/core"; +import type { z } from "zod/v4"; + +// Infer shared wire types from the public role-neutral schemas so declarations +// used by every entrypoint do not acquire a client or server package edge. +export type CallToolResult = z.infer; +export type ContentBlock = z.infer; +export type EmbeddedResource = z.infer; +export type Implementation = z.infer; +export type RequestId = z.infer; +export type ResourceLink = z.infer; +export type Tool = z.infer; diff --git a/src/server/index.ts b/src/server/index.ts index 61e7438..dc9a1f3 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -32,13 +32,12 @@ */ import { - RESOURCE_URI_META_KEY, - RESOURCE_MIME_TYPE, McpUiResourceCsp, McpUiResourceMeta, McpUiToolMeta, McpUiClientCapabilities, -} from "../app.js"; +} from "../spec.types.js"; +import { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "../constants.js"; import type { ClientCapabilities, McpServer, diff --git a/src/spec.types.ts b/src/spec.types.ts index 8b03de9..6c158d1 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -18,7 +18,7 @@ import type { RequestId, ResourceLink, Tool, -} from "@modelcontextprotocol/client"; +} from "./core-types"; /** * Current protocol version supported by this SDK. diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 041bf5e..1531bdc 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -36,12 +36,14 @@ const config = { ClientOptions: BASE_SDK_DOCS, ConnectOptions: BASE_SDK_DOCS, DiscoverResult: BASE_SDK_DOCS, + "MessageExtraInfo.classification": BASE_SDK_DOCS, ProtocolError: BASE_SDK_DOCS, "Protocol.close": BASE_SDK_DOCS, "Protocol.notification": BASE_SDK_DOCS, ResponseCacheStore: BASE_SDK_DOCS, SdkError: BASE_SDK_DOCS, "SdkErrorCode.MethodNotSupportedByProtocolVersion": BASE_SDK_DOCS, + "SdkErrorCode.UnsupportedResultType": BASE_SDK_DOCS, "__type.enforceStrictCapabilities": BASE_SDK_DOCS, }, "@modelcontextprotocol/server": { From c55a3a231fb76303376e059aef9a13367e72731c Mon Sep 17 00:00:00 2001 From: victor-openai Date: Wed, 22 Jul 2026 09:28:57 -0700 Subject: [PATCH 18/45] fix: preserve experimental capability settings (#721) --- specification/draft/apps.mdx | 8 ++--- src/app-bridge.test.ts | 10 +++++- src/generated/schema.json | 60 ++++++++++++++++++++++++++++-------- src/generated/schema.ts | 22 +++++++++---- src/spec.types.ts | 8 ++--- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index 369ef64..f2523f2 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -548,8 +548,8 @@ When the View sends an `ui/initialize` request to the Host, it MUST include its ```typescript interface McpUiAppCapabilities { - /** Experimental features (structure TBD). */ - experimental?: {}; + /** Experimental features keyed by identifier. */ + experimental?: Record; /** App exposes MCP-style tools that the host can call. */ tools?: { /** App supports tools/list_changed notifications. */ @@ -677,8 +677,8 @@ interface SupportedContentBlockModalities { } interface HostCapabilities { - /** Experimental features (structure TBD). */ - experimental?: {}; + /** Experimental features keyed by identifier. */ + experimental?: Record; /** Host supports opening external URLs. */ openLinks?: {}; /** Host supports file downloads via ui/download-file. */ diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index a4fe1de..d327c95 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -45,6 +45,9 @@ function createMockClient( const testHostInfo = { name: "TestHost", version: "1.0.0" }; const testAppInfo = { name: "TestApp", version: "1.0.0" }; const testHostCapabilities: McpUiHostCapabilities = { + experimental: { + "com.example/host-extension": { version: 1 }, + }, openLinks: {}, serverTools: {}, logging: {}, @@ -97,7 +100,12 @@ describe("App <-> AppBridge integration", () => { }); it("Bridge receives app info and capabilities after initialization", async () => { - const appCapabilities = { tools: { listChanged: true } }; + const appCapabilities = { + experimental: { + "com.example/app-extension": { version: 1 }, + }, + tools: { listChanged: true }, + }; app = new App(testAppInfo, appCapabilities, { autoResize: false }); await bridge.connect(bridgeTransport); diff --git a/src/generated/schema.json b/src/generated/schema.json index 80b4ac6..6b66482 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -9,10 +9,19 @@ "type": "object", "properties": { "experimental": { - "description": "Experimental features (structure TBD).", + "description": "Experimental features keyed by identifier.", "type": "object", - "properties": {}, - "additionalProperties": false + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {}, + "description": "Experimental features keyed by identifier." + } }, "tools": { "description": "App exposes MCP-style tools that the host can call.", @@ -298,10 +307,19 @@ "type": "object", "properties": { "experimental": { - "description": "Experimental features (structure TBD).", + "description": "Experimental features keyed by identifier.", "type": "object", - "properties": {}, - "additionalProperties": false + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {}, + "description": "Experimental features keyed by identifier." + } }, "openLinks": { "description": "Host supports opening external URLs.", @@ -2537,10 +2555,19 @@ "type": "object", "properties": { "experimental": { - "description": "Experimental features (structure TBD).", + "description": "Experimental features keyed by identifier.", "type": "object", - "properties": {}, - "additionalProperties": false + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {}, + "description": "Experimental features keyed by identifier." + } }, "tools": { "description": "App exposes MCP-style tools that the host can call.", @@ -2651,10 +2678,19 @@ "type": "object", "properties": { "experimental": { - "description": "Experimental features (structure TBD).", + "description": "Experimental features keyed by identifier.", "type": "object", - "properties": {}, - "additionalProperties": false + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {}, + "description": "Experimental features keyed by identifier." + } }, "openLinks": { "description": "Host supports opening external URLs.", diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 4368737..a8bc1b6 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -496,11 +496,16 @@ export const McpUiRequestTeardownNotificationSchema = z.object({ * @see {@link McpUiInitializeResult `McpUiInitializeResult`} for the initialization result that includes these capabilities */ export const McpUiHostCapabilitiesSchema = z.object({ - /** @description Experimental features (structure TBD). */ + /** @description Experimental features keyed by identifier. */ experimental: z - .object({}) + .record( + z.string(), + z + .record(z.string(), z.any()) + .describe("Experimental features keyed by identifier."), + ) .optional() - .describe("Experimental features (structure TBD)."), + .describe("Experimental features keyed by identifier."), /** @description Host supports opening external URLs. */ openLinks: z .object({}) @@ -583,11 +588,16 @@ export const McpUiHostCapabilitiesSchema = z.object({ * @see {@link McpUiInitializeRequest `McpUiInitializeRequest`} for the initialization request that includes these capabilities */ export const McpUiAppCapabilitiesSchema = z.object({ - /** @description Experimental features (structure TBD). */ + /** @description Experimental features keyed by identifier. */ experimental: z - .object({}) + .record( + z.string(), + z + .record(z.string(), z.any()) + .describe("Experimental features keyed by identifier."), + ) .optional() - .describe("Experimental features (structure TBD)."), + .describe("Experimental features keyed by identifier."), /** @description App exposes MCP-style tools that the host can call. */ tools: z .object({ diff --git a/src/spec.types.ts b/src/spec.types.ts index 7a8b337..bedc1f5 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -492,8 +492,8 @@ export interface McpUiRequestTeardownNotification { * @see {@link McpUiInitializeResult `McpUiInitializeResult`} for the initialization result that includes these capabilities */ export interface McpUiHostCapabilities { - /** @description Experimental features (structure TBD). */ - experimental?: {}; + /** @description Experimental features keyed by identifier. */ + experimental?: Record; /** @description Host supports opening external URLs. */ openLinks?: {}; /** @description Host supports file downloads via ui/download-file. */ @@ -536,8 +536,8 @@ export interface McpUiHostCapabilities { * @see {@link McpUiInitializeRequest `McpUiInitializeRequest`} for the initialization request that includes these capabilities */ export interface McpUiAppCapabilities { - /** @description Experimental features (structure TBD). */ - experimental?: {}; + /** @description Experimental features keyed by identifier. */ + experimental?: Record; /** @description App exposes MCP-style tools that the host can call. */ tools?: { /** @description App supports tools/list_changed notifications. */ From 7d498079e8f2d0d385ac2ec88a72e031378e6702 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Wed, 22 Jul 2026 19:34:30 +0100 Subject: [PATCH 19/45] chore: clear npm audit findings (15 -> 3) (#725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-breaking transitive updates via `npm audit fix`, plus a `sharp` major bump. Takes `npm audit` from 15 findings (2 critical, 7 high, 4 moderate, 2 low) to 3 moderate. Critical / high: - shell-quote 1.8.3 -> 1.9.0 (GHSA-w7jw-789q-3m8p, GHSA-395f-4hp3-45gv) - @babel/core 7.28.6 -> 7.29.7 (GHSA-4x5r-pxfx-6jf8) - sharp ^0.34.5 -> ^0.35.3 (GHSA-f88m-g3jw-g9cj; libvips CVE-2026-33327, CVE-2026-33328, CVE-2026-35590, CVE-2026-35591) - brace-expansion 1.1.16 / 2.1.2 (GHSA-3jxr-9vmj-r5cp) - fast-uri 3.1.4 (GHSA-v2hh-gcrm-f6hx, GHSA-4c8g-83qw-93j6) - hono 4.12.31 (8 advisories, incl. GHSA-88fw-hqm2-52qc, GHSA-hvrm-45r6-mjfj) - linkify-it 5.0.2 (GHSA-22p9-wv53-3rq4, GHSA-v245-v573-v5vm) Plus markdown-it 14.3.0, body-parser 2.3.0, type-is 2.1.0, undici 7.28.0 and vite 6.4.3. sharp is a devDependency used only by tests/e2e/generate-grid-screenshots, which is testIgnore'd unless GENERATE_SCREENSHOTS=1. The only breaking change in 0.35 is the Node floor moving to >=20.9.0, and CI runs Node 22; the API used (png / resize / toFile) is unchanged. The 3 remaining findings are all the same advisory (GHSA-frvp-7c67-39w9, path traversal in @hono/node-server's serve-static on Windows), reported once each for @hono/node-server, @modelcontextprotocol/sdk and @modelcontextprotocol/ext-apps. The fix is in @hono/node-server 2.0.5 but sdk 1.29.0 pins ^1.19.9, so the range cannot reach it; that needs an upstream bump. It is not reachable here either way — the SDK imports only getRequestListener and serve from that package, never serve-static. --- package-lock.json | 708 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 418 insertions(+), 292 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29e9058..26be03f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "prettier": "^3.6.2", "react": "^19.2.0", "react-dom": "^19.2.0", - "sharp": "^0.34.5", + "sharp": "^0.35.3", "ts-to-zod": "^5.1.0", "tsx": "^4.21.0", "typedoc": "^0.28.14", @@ -1123,13 +1123,13 @@ "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -1138,9 +1138,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", - "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -1148,21 +1148,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", - "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -1189,14 +1189,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", - "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -1219,14 +1219,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -1246,9 +1246,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -1256,29 +1256,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1298,27 +1298,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -1326,26 +1326,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", - "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -1439,33 +1439,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", - "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -1473,13 +1473,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", - "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1550,9 +1550,9 @@ "license": "Apache-2.0" }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "dev": true, "license": "MIT", "optional": true, @@ -2036,9 +2036,9 @@ } }, "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", "dev": true, "license": "MIT", "engines": { @@ -2046,9 +2046,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -2059,19 +2059,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -2082,19 +2082,39 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -2109,9 +2129,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -2126,9 +2146,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], @@ -2143,9 +2163,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], @@ -2160,9 +2180,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], @@ -2177,9 +2197,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], @@ -2194,9 +2214,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], @@ -2211,9 +2231,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], @@ -2228,9 +2248,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], @@ -2245,9 +2265,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], @@ -2262,9 +2282,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], @@ -2275,19 +2295,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], @@ -2298,19 +2318,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], @@ -2321,19 +2341,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], @@ -2344,19 +2364,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], @@ -2367,19 +2387,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], @@ -2390,19 +2410,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], @@ -2413,19 +2433,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], @@ -2436,39 +2456,56 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@emnapi/runtime": "^1.11.1" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -2479,16 +2516,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -2499,16 +2536,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -2519,7 +2556,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -3026,9 +3063,9 @@ } }, "node_modules/@oclif/core/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -4460,13 +4497,16 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.18", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", - "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz", + "integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/bezier-js": { @@ -4494,20 +4534,20 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -4517,6 +4557,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/body-parser/node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -4540,9 +4593,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -4564,9 +4617,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -4584,11 +4637,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -4597,6 +4650,34 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/browserslist/node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/browserslist/node_modules/electron-to-chromium": { + "version": "1.5.395", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz", + "integrity": "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==", + "dev": true, + "license": "ISC" + }, "node_modules/bun": { "version": "1.3.10", "resolved": "https://registry.npmjs.org/bun/-/bun-1.3.10.tgz", @@ -5005,15 +5086,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz", + "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", + "shell-quote": "1.9.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -5835,9 +5916,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -5868,9 +5949,9 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -6171,9 +6252,9 @@ } }, "node_modules/hono": { - "version": "4.12.23", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", - "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", + "version": "4.12.31", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.31.tgz", + "integrity": "sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -6576,10 +6657,20 @@ } }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" @@ -6791,15 +6882,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -6987,11 +7088,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/nodemon": { "version": "3.1.11", @@ -7615,9 +7719,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -7700,48 +7804,53 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "dev": true, - "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/shebang-command": { @@ -7766,9 +7875,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -9057,17 +9166,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typedoc": { @@ -9108,9 +9234,9 @@ } }, "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -9162,9 +9288,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", - "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -9227,9 +9353,9 @@ } }, "node_modules/vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", - "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 8c6ead5..b494003 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "prettier": "^3.6.2", "react": "^19.2.0", "react-dom": "^19.2.0", - "sharp": "^0.34.5", + "sharp": "^0.35.3", "ts-to-zod": "^5.1.0", "tsx": "^4.21.0", "typedoc": "^0.28.14", From e7ffe7c738b2db36d76339ec2f1b0d9c16c29812 Mon Sep 17 00:00:00 2001 From: Enrico <2827496+tonxxd@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:24:49 -0700 Subject: [PATCH 20/45] chore: update MCP SDK packages to beta.5 --- examples/basic-host/package.json | 2 +- examples/basic-server-preact/package.json | 8 +- examples/basic-server-react/package.json | 8 +- examples/basic-server-solid/package.json | 8 +- examples/basic-server-svelte/package.json | 8 +- examples/basic-server-vanillajs/package.json | 8 +- examples/basic-server-vue/package.json | 8 +- examples/budget-allocator-server/package.json | 6 +- examples/cohort-heatmap-server/package.json | 6 +- .../customer-segmentation-server/package.json | 6 +- examples/debug-server/package.json | 6 +- examples/integration-server/package.json | 8 +- examples/lazy-auth-server/package.json | 4 +- examples/map-server/package.json | 8 +- examples/pdf-server/package.json | 8 +- examples/quickstart/package.json | 6 +- examples/scenario-modeler-server/package.json | 6 +- examples/shadertoy-server/package.json | 6 +- examples/sheet-music-server/package.json | 6 +- examples/system-monitor-server/package.json | 6 +- examples/threejs-server/package.json | 8 +- examples/transcript-server/package.json | 6 +- examples/video-resource-server/package.json | 8 +- examples/wiki-explorer-server/package.json | 8 +- package-lock.json | 613 +++--------------- package.json | 17 +- src/generated/schema.json | 56 +- 27 files changed, 235 insertions(+), 613 deletions(-) diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index 583d2ff..c2a104e 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "react": "^19.2.0", "react-dom": "^19.2.0", "zod": "^4.1.13" diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 0187827..8722a54 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index dae7665..0a860c5 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -34,11 +34,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index 5d91407..a0d1bfa 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index 18bf7ab..81d73fe 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 6bb75af..1958b73 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index 3b34aa6..a872c52 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 5d4fe8e..3b02ddf 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index c54040a..fd4777c 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index ef14b32..0846b40 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index 0e3f508..f118717 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -35,9 +35,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "zod": "^4.1.13" }, "devDependencies": { diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json index 56fc8ed..2993020 100644 --- a/examples/integration-server/package.json +++ b/examples/integration-server/package.json @@ -15,11 +15,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index 6aef3e1..257f96c 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -23,8 +23,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "jose": "^6.0.0", diff --git a/examples/map-server/package.json b/examples/map-server/package.json index a3639ce..0a56f85 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -26,11 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index a7eb4bb..ac80173 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -26,11 +26,11 @@ }, "dependencies": { "@cantoo/pdf-lib": "^2.6.5", - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index f741c6e..96bcbfa 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -16,9 +16,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index 63a5d48..93b6d8a 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index 318f017..ef5f563 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -25,9 +25,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index 10d7c71..0628f58 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -25,9 +25,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index 5bc4a95..c5aa008 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index fb97d33..aae13a7 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -26,11 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index eea4a80..51e7d27 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -25,9 +25,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index ea816b9..e8535cd 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index bd31d6f..11ec18c 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -26,11 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.4", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/package-lock.json b/package-lock.json index c7d677e..e6e524e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ }, "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "https://pkg.pr.new/@modelcontextprotocol/client@2501", - "@modelcontextprotocol/core": "https://pkg.pr.new/@modelcontextprotocol/core@2501", - "@modelcontextprotocol/server": "https://pkg.pr.new/@modelcontextprotocol/server@2501", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/core": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -52,9 +52,9 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/core": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^4.2.0" @@ -78,7 +78,7 @@ "name": "@modelcontextprotocol/ext-apps-basic-host", "version": "1.7.4", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -99,33 +99,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-host/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-host/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-preact": { "name": "@modelcontextprotocol/server-basic-preact", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", @@ -146,33 +129,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-preact/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-preact/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-react": { "name": "@modelcontextprotocol/server-basic-react", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -196,33 +162,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-react/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-react/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-solid": { "name": "@modelcontextprotocol/server-basic-solid", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", @@ -243,33 +192,16 @@ "vite-plugin-solid": "^2.11.12" } }, - "examples/basic-server-solid/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-solid/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-svelte": { "name": "@modelcontextprotocol/server-basic-svelte", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", @@ -290,33 +222,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-svelte/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-svelte/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-vanillajs": { "name": "@modelcontextprotocol/server-basic-vanillajs", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -335,33 +250,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-vanillajs/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-vanillajs/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/basic-server-vue": { "name": "@modelcontextprotocol/server-basic-vue", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", @@ -382,32 +280,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/basic-server-vue/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/basic-server-vue/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/budget-allocator-server": { "name": "@modelcontextprotocol/server-budget-allocator", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -427,32 +308,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/budget-allocator-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/budget-allocator-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/cohort-heatmap-server": { "name": "@modelcontextprotocol/server-cohort-heatmap", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -476,32 +340,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/cohort-heatmap-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/cohort-heatmap-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/customer-segmentation-server": { "name": "@modelcontextprotocol/server-customer-segmentation", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -521,32 +368,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/customer-segmentation-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/customer-segmentation-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/debug-server": { "name": "@modelcontextprotocol/server-debug", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "zod": "^4.1.13" }, "bin": { @@ -565,31 +395,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/debug-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/debug-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/integration-server": { "version": "1.7.4", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -612,31 +425,14 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/integration-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/integration-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/lazy-auth-server": { "name": "@modelcontextprotocol/server-lazy-auth", "version": "1.7.4", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "jose": "^6.0.0", @@ -656,33 +452,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/lazy-auth-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/lazy-auth-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/map-server": { "name": "@modelcontextprotocol/server-map", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -701,34 +480,17 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/map-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/map-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/pdf-server": { "name": "@modelcontextprotocol/server-pdf", "version": "1.7.4", "license": "MIT", "dependencies": { "@cantoo/pdf-lib": "^2.6.5", - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", @@ -748,23 +510,6 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/pdf-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/pdf-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", "version": "1.7.4", @@ -777,10 +522,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -797,16 +542,6 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/quickstart/node_modules/@types/node": { - "version": "22.19.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.5.tgz", - "integrity": "sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", "version": "1.7.4", @@ -820,10 +555,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -848,32 +583,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/scenario-modeler-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/scenario-modeler-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/shadertoy-server": { "name": "@modelcontextprotocol/server-shadertoy", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -892,32 +610,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/shadertoy-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/shadertoy-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/sheet-music-server": { "name": "@modelcontextprotocol/server-sheet-music", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", @@ -937,32 +638,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/sheet-music-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/sheet-music-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/system-monitor-server": { "name": "@modelcontextprotocol/server-system-monitor", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -983,33 +667,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/system-monitor-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/system-monitor-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/threejs-server": { "name": "@modelcontextprotocol/server-threejs", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -1035,32 +702,15 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/threejs-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/threejs-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/transcript-server": { "name": "@modelcontextprotocol/server-transcript", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -1080,33 +730,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/transcript-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/transcript-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/video-resource-server": { "name": "@modelcontextprotocol/server-video-resource", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -1125,33 +758,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/video-resource-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/video-resource-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "examples/wiki-explorer-server": { "name": "@modelcontextprotocol/server-wiki-explorer", "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/express": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0-beta.5", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/node": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -1172,23 +788,6 @@ "vite-plugin-singlefile": "^2.3.0" } }, - "examples/wiki-explorer-server/node_modules/@types/node": { - "version": "22.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", - "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "examples/wiki-explorer-server/node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/code-frame": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", @@ -2644,12 +2243,12 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/client": { - "version": "2.0.0-beta.4", - "resolved": "https://pkg.pr.new/@modelcontextprotocol/client@2501", - "integrity": "sha512-5FvmSf2qWlJXlI9ydf8230f1rsDHMEw9Jgcq2ZUGmA3DJShaYV6t49zKJBuawetMcLOzWBGs8+qDzhZOZTk6Fw==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.5.tgz", + "integrity": "sha512-YuuNm5f2TMoFQRje1UqVP8TJRjijCXMz4ckvoVpx1cUXuBEmykWQ2d8R536pek6UKcXT41T5nWc4qR1JFIbEmg==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/core": "https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/core@f60b401", + "@modelcontextprotocol/core": "2.0.0-beta.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", @@ -2662,9 +2261,9 @@ } }, "node_modules/@modelcontextprotocol/core": { - "version": "2.0.0-beta.4", - "resolved": "https://pkg.pr.new/@modelcontextprotocol/core@2501", - "integrity": "sha512-nsMXd4wQBKzmph6r+WOhum+mXjDYljTAqwY/XUg3hLtvNOQ8+JVqBSJOVCMJvx9lXhTpTOPrGZ3BuNiaNPjSvg==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.5.tgz", + "integrity": "sha512-HKbY9XTbsDy1Y6r2I55TGE3JEapM0vg96e1MUmBIF9LGjos5gjhcIrTz1yvBPLg2aFKHjwhUAQfRdrCEnPxNew==", "license": "MIT", "dependencies": { "zod": "^4.2.0" @@ -2674,9 +2273,9 @@ } }, "node_modules/@modelcontextprotocol/express": { - "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/express/-/express-2.0.0-beta.4.tgz", - "integrity": "sha512-7hI8bEomz6rMn8sKYRVIZgQJsrWIjIS9WiPyIp3X2pMyLSCC7hB5km0kg+9SygWygTIRnPfte0tXevRB3mXEHQ==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/express/-/express-2.0.0-beta.5.tgz", + "integrity": "sha512-SDHAAKCOkHrxuBRyh6fRQJpjWBt5vrRCmvV7OIyWdmIksQzmTYsw7oEX1p9Kje6PSDqKJHpmqLJzZNUnOhrfCQ==", "license": "MIT", "dependencies": { "cors": "^2.8.5" @@ -2685,7 +2284,7 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/server": "^2.0.0-beta.4", + "@modelcontextprotocol/server": "^2.0.0-beta.5", "express": "^4.18.0 || ^5.0.0" } }, @@ -2723,9 +2322,9 @@ "link": true }, "node_modules/@modelcontextprotocol/node": { - "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/node/-/node-2.0.0-beta.4.tgz", - "integrity": "sha512-OqbrRVNYNfLxBB2BRkP5oIHKHa0IPHluMbaLgw7YCRKcj5be9ExzyWFJY3KVMb+1F2AVn+0SRoH0XKcNzh3HYQ==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/node/-/node-2.0.0-beta.5.tgz", + "integrity": "sha512-flrOzx5qFLBM9I8IYRGfYUY02erFmxOvYQ9sEE6iSj/ZZpRYKorXWscM/qrfW9rwlcGLyymHhnP2AaDbMB1lIA==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9" @@ -2734,7 +2333,7 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/server": "^2.0.0-beta.4", + "@modelcontextprotocol/server": "^2.0.0-beta.5", "hono": "^4.11.4" }, "peerDependenciesMeta": { @@ -2789,12 +2388,12 @@ } }, "node_modules/@modelcontextprotocol/server": { - "version": "2.0.0-beta.4", - "resolved": "https://pkg.pr.new/@modelcontextprotocol/server@2501", - "integrity": "sha512-mI+V52bTdSCHDrsrVG3TeOzgy+apDQXz6l1B4KuwKV/8xbRAsV9LZ2Nf67SEoLszuYYH0as4pZQUG4cCMYP5/g==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.5.tgz", + "integrity": "sha512-i1E5l75rQKsgY/AKAIspgMBH1vEL7dqiK7tHr0L+raYcb0SWOziqNGJXGIG6NY4AlXDWIKGJQGB7Nqfs3oUi5g==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/core": "https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/core@f60b401", + "@modelcontextprotocol/core": "2.0.0-beta.5", "zod": "^4.2.0" }, "engines": { diff --git a/package.json b/package.json index 4f88483..ac7897c 100644 --- a/package.json +++ b/package.json @@ -78,9 +78,9 @@ "author": "Olivier Chafik", "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "https://pkg.pr.new/@modelcontextprotocol/client@2501", - "@modelcontextprotocol/core": "https://pkg.pr.new/@modelcontextprotocol/core@2501", - "@modelcontextprotocol/server": "https://pkg.pr.new/@modelcontextprotocol/server@2501", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/core": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -111,9 +111,9 @@ "zod": "^4.2.0" }, "peerDependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.4", - "@modelcontextprotocol/core": "2.0.0-beta.4", - "@modelcontextprotocol/server": "2.0.0-beta.4", + "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/core": "2.0.0-beta.5", + "@modelcontextprotocol/server": "2.0.0-beta.5", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^4.2.0" @@ -136,10 +136,7 @@ "seroval": "1.4.1", "seroval-plugins": "1.4.2", "solid-js": "1.9.10", - "@types/node": "20.19.27", - "@modelcontextprotocol/core": "https://pkg.pr.new/@modelcontextprotocol/core@2501", - "@modelcontextprotocol/client": "https://pkg.pr.new/@modelcontextprotocol/client@2501", - "@modelcontextprotocol/server": "https://pkg.pr.new/@modelcontextprotocol/server@2501" + "@types/node": "20.19.27" }, "dependencies": { "@standard-schema/spec": "^1.1.0" diff --git a/src/generated/schema.json b/src/generated/schema.json index 5f2124a..0932dc4 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -5384,26 +5384,52 @@ "_meta": { "type": "object", "properties": { - "progressToken": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "integer", - "minimum": -9007199254740991, - "maximum": 9007199254740991 - } - ] - }, - "io.modelcontextprotocol/related-task": { + "io.modelcontextprotocol/serverInfo": { "type": "object", "properties": { - "taskId": { + "name": { + "type": "string" + }, + "title": { + "type": "string" + }, + "icons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "sizes": { + "type": "array", + "items": { + "type": "string" + } + }, + "theme": { + "type": "string", + "enum": ["light", "dark"] + } + }, + "required": ["src"], + "additionalProperties": false + } + }, + "version": { + "type": "string" + }, + "websiteUrl": { + "type": "string" + }, + "description": { "type": "string" } }, - "required": ["taskId"], + "required": ["name", "version"], "additionalProperties": false } }, From c847bc0887d23c90f79a7acfa95eb32af29e0833 Mon Sep 17 00:00:00 2001 From: Enrico <2827496+tonxxd@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:37:22 -0700 Subject: [PATCH 21/45] docs: align Protocol and role-specific setup --- README.md | 22 ++++++++++++++----- docs/overview.md | 10 ++++----- docs/quickstart.md | 2 +- .../skills/add-app-to-server/SKILL.md | 2 +- .../mcp-apps/skills/convert-web-app/SKILL.md | 2 +- .../mcp-apps/skills/create-mcp-app/SKILL.md | 2 +- src/app-bridge.ts | 10 ++++----- src/app.ts | 7 ++++-- 8 files changed, 35 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b3f68e7..a514e19 100644 --- a/README.md +++ b/README.md @@ -121,17 +121,27 @@ resources: ## Getting Started +For a View or host: + ```bash npm install -S @modelcontextprotocol/ext-apps \ - @modelcontextprotocol/client@2.0.0-beta.4 \ - @modelcontextprotocol/server@2.0.0-beta.4 \ - @modelcontextprotocol/core@2.0.0-beta.4 \ + @modelcontextprotocol/client@2.0.0-beta.5 \ + @modelcontextprotocol/core@2.0.0-beta.5 \ zod@^4.2.0 ``` -This release uses the split base MCP SDK v2 packages. Install all three at the -exact published beta.4 version so the Apps SDK and your MCP client/server share -one compatible protocol implementation. +For an MCP server: + +```bash +npm install -S @modelcontextprotocol/ext-apps \ + @modelcontextprotocol/server@2.0.0-beta.5 \ + @modelcontextprotocol/core@2.0.0-beta.5 \ + zod@^4.2.0 +``` + +Applications that implement both roles should install both `client` and +`server`. Keep all installed base MCP SDK packages on the exact same published +beta so they share one compatible protocol implementation. **New here?** Start with the [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html) diff --git a/docs/overview.md b/docs/overview.md index 63156ec..5f3852d 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -56,11 +56,11 @@ flowchart LR - **Host** — The chat client (e.g., Claude Desktop) that connects to servers, embeds Views in iframes, and proxies communication between them. - **View** — The UI running inside a sandboxed iframe. It receives tool data from the Host and can call server tools or send messages back to the chat. -The View's `App` subclasses the base MCP SDK `Client`. The Host's `AppBridge` -subclasses the base MCP SDK `Server` for the inner iframe channel, while a -separate outer `Client` connects the Host to the actual MCP Server. Keeping -those two connections separate prevents iframe negotiation and capabilities -from leaking into the server connection. +The View's `App` and the Host's `AppBridge` both subclass the base MCP SDK's +public `Protocol` for the iframe channel. A separate outer `Client` connects +the Host to the actual MCP Server. Keeping those two connections separate +preserves the Apps-only iframe handshake and prevents iframe capabilities from +leaking into the server connection. ## Lifecycle diff --git a/docs/quickstart.md b/docs/quickstart.md index eb6b096..3cac872 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -37,7 +37,7 @@ Install the dependencies you'll need: ```bash npm init -y -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 @modelcontextprotocol/node@2.0.0-beta.4 @modelcontextprotocol/express@2.0.0-beta.4 zod@^4.2.0 express cors +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 @modelcontextprotocol/node@2.0.0-beta.5 @modelcontextprotocol/express@2.0.0-beta.5 zod@^4.2.0 express cors npm install -D typescript vite vite-plugin-singlefile @types/express @types/cors @types/node tsx concurrently cross-env ``` diff --git a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md index 69e41f8..fda72b0 100644 --- a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md +++ b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md @@ -77,7 +77,7 @@ Before writing any code, analyze the server's existing tools and determine which ## Step 2: Add Dependencies ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 zod@^4.2.0 +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 zod@^4.2.0 npm install -D vite vite-plugin-singlefile ``` diff --git a/plugins/mcp-apps/skills/convert-web-app/SKILL.md b/plugins/mcp-apps/skills/convert-web-app/SKILL.md index 22686ef..03fc8d7 100644 --- a/plugins/mcp-apps/skills/convert-web-app/SKILL.md +++ b/plugins/mcp-apps/skills/convert-web-app/SKILL.md @@ -115,7 +115,7 @@ Create a new MCP server with tool and resource registration. This wraps the exis ### Dependencies ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 zod@^4.2.0 +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 zod@^4.2.0 npm install -D tsx vite vite-plugin-singlefile ``` diff --git a/plugins/mcp-apps/skills/create-mcp-app/SKILL.md b/plugins/mcp-apps/skills/create-mcp-app/SKILL.md index fc41e70..6f1c466 100644 --- a/plugins/mcp-apps/skills/create-mcp-app/SKILL.md +++ b/plugins/mcp-apps/skills/create-mcp-app/SKILL.md @@ -110,7 +110,7 @@ See `/tmp/mcp-ext-apps/docs/patterns.md` for detailed recipes: **Always** use `npm install` to add dependencies rather than manually writing version numbers: ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 @modelcontextprotocol/node@2.0.0-beta.4 @modelcontextprotocol/express@2.0.0-beta.4 zod@^4.2.0 express cors +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 @modelcontextprotocol/node@2.0.0-beta.5 @modelcontextprotocol/express@2.0.0-beta.5 zod@^4.2.0 express cors npm install -D typescript vite vite-plugin-singlefile concurrently cross-env @types/node @types/express @types/cors ``` diff --git a/src/app-bridge.ts b/src/app-bridge.ts index d6a2368..59ea586 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -241,11 +241,11 @@ export type AppBridgeEventMap = { /** * Host-side bridge for communicating with a single View ({@link app!App `App`}). * - * `AppBridge` extends the base MCP SDK's public `Server` class and acts as a proxy between - * the host application and a view running in an iframe. When an MCP client - * is provided to the constructor, it automatically forwards MCP server capabilities - * (tools, resources, prompts) to the view. It also handles the initialization - * handshake. + * `AppBridge` extends the base MCP SDK's public `Protocol` class and acts as a + * proxy between the host application and a view running in an iframe. When a + * separate MCP client is provided to the constructor, it automatically forwards + * MCP server capabilities (tools, resources, prompts) to the view. It also + * handles the Apps initialization handshake. * * ## Architecture * diff --git a/src/app.ts b/src/app.ts index 0ff369e..34ef284 100644 --- a/src/app.ts +++ b/src/app.ts @@ -297,7 +297,8 @@ export type AppEventMap = { * * The `App` class provides a framework-agnostic way to build interactive MCP Apps * that run inside host applications. It extends the base MCP SDK's public - * `Client` class and adds the Apps handshake, lifecycle, and high-level APIs. + * `Protocol` class, composes an {@link EventDispatcher}, and adds the Apps + * handshake, lifecycle, and high-level APIs. * * ## Architecture * @@ -314,9 +315,11 @@ export type AppEventMap = { * * ## Protocol and event methods * - * As a subclass of the base MCP SDK's `Client`, `App` provides: + * From the base MCP SDK's `Protocol`, `App` provides: * - `setRequestHandler()` - Register handlers for requests from host * - `setNotificationHandler()` - Register handlers for notifications from host + * + * From its composed {@link EventDispatcher}, `App` also provides: * - `addEventListener()` - Append a listener for a notification event (multi-listener) * - `removeEventListener()` - Remove a previously added listener * From 92f46a574568a3ddac7600343b7d3c4c4ed7b588 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Thu, 23 Jul 2026 12:25:07 +0100 Subject: [PATCH 22/45] chore: bump ext-apps to 1.7.5 (#724) --- examples/basic-host/package.json | 2 +- examples/basic-server-preact/package.json | 2 +- examples/basic-server-react/package.json | 2 +- examples/basic-server-solid/package.json | 2 +- examples/basic-server-svelte/package.json | 2 +- examples/basic-server-vanillajs/package.json | 2 +- examples/basic-server-vue/package.json | 2 +- examples/budget-allocator-server/package.json | 2 +- examples/cohort-heatmap-server/package.json | 2 +- .../customer-segmentation-server/package.json | 2 +- examples/debug-server/package.json | 2 +- examples/integration-server/package.json | 2 +- examples/lazy-auth-server/package.json | 2 +- examples/map-server/package.json | 2 +- examples/pdf-server/package.json | 2 +- examples/qr-server/package.json | 2 +- examples/quickstart/package.json | 2 +- examples/say-server/package.json | 2 +- examples/scenario-modeler-server/package.json | 2 +- examples/shadertoy-server/package.json | 2 +- examples/sheet-music-server/package.json | 2 +- examples/system-monitor-server/package.json | 2 +- examples/threejs-server/package.json | 2 +- examples/transcript-server/package.json | 2 +- examples/video-resource-server/package.json | 2 +- examples/wiki-explorer-server/package.json | 2 +- package-lock.json | 56 +++++++++---------- package.json | 2 +- 28 files changed, 55 insertions(+), 55 deletions(-) diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index 1b45dcf..f54b237 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -1,7 +1,7 @@ { "homepage": "https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host", "name": "@modelcontextprotocol/ext-apps-basic-host", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "scripts": { "build": "tsc --noEmit && concurrently \"cross-env INPUT=index.html vite build\" \"cross-env INPUT=sandbox.html vite build\"", diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 5127202..81ed0de 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-preact", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Basic MCP App Server example using Preact", "repository": { diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index e4d1233..5ae783e 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-react", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Basic MCP App Server example using React", "repository": { diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index 8f75ac8..c2950d9 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-solid", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Basic MCP App Server example using Solid", "repository": { diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index f877bf7..8238feb 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-svelte", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Basic MCP App Server example using Svelte", "repository": { diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 5d47f2c..8ca330e 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-vanillajs", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Basic MCP App Server example using vanilla JavaScript", "repository": { diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index d27ddc9..9c0eb7b 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-vue", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Basic MCP App Server example using Vue", "repository": { diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 19a1d25..0cd94d0 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-budget-allocator", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Budget allocator MCP App Server with interactive visualization", "repository": { diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index 8d1268b..9988679 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-cohort-heatmap", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Cohort heatmap MCP App Server for retention analysis", "repository": { diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index 4511de9..2fc85f9 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-customer-segmentation", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Customer segmentation MCP App Server with filtering", "repository": { diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index 3417a8f..16d2f4c 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-debug", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Debug MCP App Server for testing all SDK capabilities", "repository": { diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json index f6715a4..a8b5421 100644 --- a/examples/integration-server/package.json +++ b/examples/integration-server/package.json @@ -1,6 +1,6 @@ { "name": "integration-server", - "version": "1.7.4", + "version": "1.7.5", "private": true, "type": "module", "scripts": { diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index f3ed6cd..8e96906 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-lazy-auth", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP App example demonstrating lazy (on-demand) OAuth: public tools work unauthenticated, protected tools return 401 + WWW-Authenticate so the host runs the OAuth flow only when needed", "repository": { diff --git a/examples/map-server/package.json b/examples/map-server/package.json index 5dc23fe..5de1754 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-map", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP App Server example with CesiumJS 3D globe and geocoding", "repository": { diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index 4d960f1..8942a90 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-pdf", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP server for loading and extracting text from PDF files with chunked pagination and interactive viewer", "repository": { diff --git a/examples/qr-server/package.json b/examples/qr-server/package.json index a5cbf5b..8152866 100644 --- a/examples/qr-server/package.json +++ b/examples/qr-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-qr", - "version": "1.7.4", + "version": "1.7.5", "private": true, "scripts": { "start": "uv run server.py", diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index 99948cc..e1dcf2d 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/quickstart", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "private": true, "description": "Quickstart MCP App Server example", diff --git a/examples/say-server/package.json b/examples/say-server/package.json index a0076e6..d51dabd 100644 --- a/examples/say-server/package.json +++ b/examples/say-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-say", - "version": "1.7.4", + "version": "1.7.5", "private": true, "description": "Streaming TTS MCP App Server with karaoke-style text highlighting", "repository": { diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index aa1e668..716c7b5 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-scenario-modeler", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Financial scenario modeling MCP App Server", "repository": { diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index ccf15a4..0505f47 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-shadertoy", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP App Server example for rendering ShaderToy-compatible GLSL shaders", "repository": { diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index ca9fa90..e9e39cc 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-sheet-music", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP App Server for rendering and playing sheet music from ABC notation", "repository": { diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index 12e8cea..9506c61 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-system-monitor", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "System monitor MCP App Server with real-time stats", "repository": { diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index cfb5686..2121460 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-threejs", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Three.js 3D visualization MCP App Server", "repository": { diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index dcba3d7..8e9d495 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-transcript", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP App Server for live speech transcription", "repository": { diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index b0d04af..8d8b8ec 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-video-resource", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "MCP App Server demonstrating video resources served as base64 blobs", "repository": { diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index fe511ad..af02eae 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-wiki-explorer", - "version": "1.7.4", + "version": "1.7.5", "type": "module", "description": "Wikipedia link explorer MCP App Server with graph visualization", "repository": { diff --git a/package-lock.json b/package-lock.json index 26be03f..9dc76e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@modelcontextprotocol/ext-apps", - "version": "1.7.4", + "version": "1.7.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@modelcontextprotocol/ext-apps", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "workspaces": [ "examples/*" @@ -66,7 +66,7 @@ }, "examples/basic-host": { "name": "@modelcontextprotocol/ext-apps-basic-host", - "version": "1.7.4", + "version": "1.7.5", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/sdk": "^1.29.0", @@ -108,7 +108,7 @@ }, "examples/basic-server-preact": { "name": "@modelcontextprotocol/server-basic-preact", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -152,7 +152,7 @@ }, "examples/basic-server-react": { "name": "@modelcontextprotocol/server-basic-react", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -199,7 +199,7 @@ }, "examples/basic-server-solid": { "name": "@modelcontextprotocol/server-basic-solid", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -243,7 +243,7 @@ }, "examples/basic-server-svelte": { "name": "@modelcontextprotocol/server-basic-svelte", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -287,7 +287,7 @@ }, "examples/basic-server-vanillajs": { "name": "@modelcontextprotocol/server-basic-vanillajs", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -329,7 +329,7 @@ }, "examples/basic-server-vue": { "name": "@modelcontextprotocol/server-basic-vue", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -373,7 +373,7 @@ }, "examples/budget-allocator-server": { "name": "@modelcontextprotocol/server-budget-allocator", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -416,7 +416,7 @@ }, "examples/cohort-heatmap-server": { "name": "@modelcontextprotocol/server-cohort-heatmap", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -463,7 +463,7 @@ }, "examples/customer-segmentation-server": { "name": "@modelcontextprotocol/server-customer-segmentation", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -506,7 +506,7 @@ }, "examples/debug-server": { "name": "@modelcontextprotocol/server-debug", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -547,7 +547,7 @@ "license": "MIT" }, "examples/integration-server": { - "version": "1.7.4", + "version": "1.7.5", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/sdk": "^1.29.0", @@ -592,7 +592,7 @@ }, "examples/lazy-auth-server": { "name": "@modelcontextprotocol/server-lazy-auth", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -634,7 +634,7 @@ }, "examples/map-server": { "name": "@modelcontextprotocol/server-map", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -676,7 +676,7 @@ }, "examples/pdf-server": { "name": "@modelcontextprotocol/server-pdf", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@cantoo/pdf-lib": "^2.6.5", @@ -720,7 +720,7 @@ }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", - "version": "1.7.4", + "version": "1.7.5", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/sdk": "^1.29.0" @@ -728,7 +728,7 @@ }, "examples/quickstart": { "name": "@modelcontextprotocol/quickstart", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -760,7 +760,7 @@ }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -769,7 +769,7 @@ }, "examples/scenario-modeler-server": { "name": "@modelcontextprotocol/server-scenario-modeler", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -817,7 +817,7 @@ }, "examples/shadertoy-server": { "name": "@modelcontextprotocol/server-shadertoy", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -859,7 +859,7 @@ }, "examples/sheet-music-server": { "name": "@modelcontextprotocol/server-sheet-music", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -902,7 +902,7 @@ }, "examples/system-monitor-server": { "name": "@modelcontextprotocol/server-system-monitor", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -946,7 +946,7 @@ }, "examples/threejs-server": { "name": "@modelcontextprotocol/server-threejs", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -995,7 +995,7 @@ }, "examples/transcript-server": { "name": "@modelcontextprotocol/server-transcript", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -1038,7 +1038,7 @@ }, "examples/video-resource-server": { "name": "@modelcontextprotocol/server-video-resource", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", @@ -1080,7 +1080,7 @@ }, "examples/wiki-explorer-server": { "name": "@modelcontextprotocol/server-wiki-explorer", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", diff --git a/package.json b/package.json index b494003..15ef5d7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "url": "https://github.com/modelcontextprotocol/ext-apps" }, "homepage": "https://github.com/modelcontextprotocol/ext-apps", - "version": "1.7.4", + "version": "1.7.5", "license": "MIT", "description": "MCP Apps SDK — Enable MCP servers to display interactive user interfaces in conversational clients.", "type": "module", From 4e4886c5ff7125f5332bd29685e94076caeaac24 Mon Sep 17 00:00:00 2001 From: Enrico <2827496+tonxxd@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:02:48 -0700 Subject: [PATCH 23/45] chore: update MCP SDK packages to stable 2.0.0 --- examples/basic-host/package.json | 2 +- examples/basic-server-preact/package.json | 8 +- examples/basic-server-react/package.json | 8 +- examples/basic-server-solid/package.json | 8 +- examples/basic-server-svelte/package.json | 8 +- examples/basic-server-vanillajs/package.json | 8 +- examples/basic-server-vue/package.json | 8 +- examples/budget-allocator-server/package.json | 6 +- examples/cohort-heatmap-server/package.json | 6 +- .../customer-segmentation-server/package.json | 6 +- examples/debug-server/package.json | 6 +- examples/integration-server/package.json | 8 +- examples/lazy-auth-server/package.json | 4 +- examples/map-server/package.json | 8 +- examples/pdf-server/package.json | 8 +- examples/quickstart/package.json | 6 +- examples/scenario-modeler-server/package.json | 6 +- examples/shadertoy-server/package.json | 6 +- examples/sheet-music-server/package.json | 6 +- examples/system-monitor-server/package.json | 6 +- examples/threejs-server/package.json | 8 +- examples/transcript-server/package.json | 6 +- examples/video-resource-server/package.json | 8 +- examples/wiki-explorer-server/package.json | 8 +- package-lock.json | 212 +++++++++--------- package.json | 12 +- 26 files changed, 193 insertions(+), 193 deletions(-) diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index c2a104e..7bce111 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "react": "^19.2.0", "react-dom": "^19.2.0", "zod": "^4.1.13" diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 8722a54..0043486 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index 0a860c5..69a06df 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -34,11 +34,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index a0d1bfa..6cfa17f 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index 81d73fe..dd88b97 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 1958b73..4a37592 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index a872c52..e8f929e 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 3b02ddf..4d2b977 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index fd4777c..0761130 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index 0846b40..6163424 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index f118717..3499abf 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -35,9 +35,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "zod": "^4.1.13" }, "devDependencies": { diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json index 2993020..64a3286 100644 --- a/examples/integration-server/package.json +++ b/examples/integration-server/package.json @@ -15,11 +15,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index 257f96c..4691a2e 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -23,8 +23,8 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "jose": "^6.0.0", diff --git a/examples/map-server/package.json b/examples/map-server/package.json index 0a56f85..4577636 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -26,11 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index ac80173..e004394 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -26,11 +26,11 @@ }, "dependencies": { "@cantoo/pdf-lib": "^2.6.5", - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index 96bcbfa..4312b75 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -16,9 +16,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index 93b6d8a..723491c 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index ef5f563..556699d 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -25,9 +25,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index 0628f58..a8075e6 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -25,9 +25,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index c5aa008..cc18d4b 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -27,9 +27,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index aae13a7..b4cc6de 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -26,11 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index 51e7d27..f3617c4 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -25,9 +25,9 @@ }, "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index e8535cd..64a07e9 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -24,11 +24,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 11ec18c..e4bd274 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -26,11 +26,11 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/express": "2.0.0-beta.5", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/package-lock.json b/package-lock.json index e6e524e..d4e84ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ }, "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/core": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -52,9 +52,9 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/core": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^4.2.0" @@ -78,7 +78,7 @@ "name": "@modelcontextprotocol/ext-apps-basic-host", "version": "1.7.4", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -104,11 +104,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", @@ -134,11 +134,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -167,11 +167,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", @@ -197,11 +197,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", @@ -227,11 +227,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -255,11 +255,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", @@ -285,10 +285,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -313,10 +313,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -345,10 +345,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -373,10 +373,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "zod": "^4.1.13" }, "bin": { @@ -398,11 +398,11 @@ "examples/integration-server": { "version": "1.7.4", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -431,8 +431,8 @@ "license": "MIT", "dependencies": { "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "jose": "^6.0.0", @@ -457,11 +457,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -486,11 +486,11 @@ "license": "MIT", "dependencies": { "@cantoo/pdf-lib": "^2.6.5", - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", @@ -522,10 +522,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -555,10 +555,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -588,10 +588,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -615,10 +615,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", @@ -643,10 +643,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -672,11 +672,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", @@ -707,10 +707,10 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -735,11 +735,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "zod": "^4.1.13" @@ -763,11 +763,11 @@ "version": "1.7.4", "license": "MIT", "dependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/express": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/express": "2.0.0", "@modelcontextprotocol/ext-apps": "^1.7.0", - "@modelcontextprotocol/node": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/node": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", @@ -2243,12 +2243,12 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/client": { - "version": "2.0.0-beta.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0-beta.5.tgz", - "integrity": "sha512-YuuNm5f2TMoFQRje1UqVP8TJRjijCXMz4ckvoVpx1cUXuBEmykWQ2d8R536pek6UKcXT41T5nWc4qR1JFIbEmg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0.tgz", + "integrity": "sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/core": "2.0.0-beta.5", + "@modelcontextprotocol/core": "2.0.0", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", @@ -2261,9 +2261,9 @@ } }, "node_modules/@modelcontextprotocol/core": { - "version": "2.0.0-beta.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0-beta.5.tgz", - "integrity": "sha512-HKbY9XTbsDy1Y6r2I55TGE3JEapM0vg96e1MUmBIF9LGjos5gjhcIrTz1yvBPLg2aFKHjwhUAQfRdrCEnPxNew==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0.tgz", + "integrity": "sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==", "license": "MIT", "dependencies": { "zod": "^4.2.0" @@ -2273,9 +2273,9 @@ } }, "node_modules/@modelcontextprotocol/express": { - "version": "2.0.0-beta.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/express/-/express-2.0.0-beta.5.tgz", - "integrity": "sha512-SDHAAKCOkHrxuBRyh6fRQJpjWBt5vrRCmvV7OIyWdmIksQzmTYsw7oEX1p9Kje6PSDqKJHpmqLJzZNUnOhrfCQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/express/-/express-2.0.0.tgz", + "integrity": "sha512-Snlr8j9FR9LcVvEJPF7qJ7d5zTL4Bes2dk7RcacN9eSZ7OLohwxqhEvWu1+UxELyScgLbLLOdeVEGdwKI1iwVQ==", "license": "MIT", "dependencies": { "cors": "^2.8.5" @@ -2284,7 +2284,7 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/server": "^2.0.0-beta.5", + "@modelcontextprotocol/server": "^2.0.0", "express": "^4.18.0 || ^5.0.0" } }, @@ -2322,9 +2322,9 @@ "link": true }, "node_modules/@modelcontextprotocol/node": { - "version": "2.0.0-beta.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/node/-/node-2.0.0-beta.5.tgz", - "integrity": "sha512-flrOzx5qFLBM9I8IYRGfYUY02erFmxOvYQ9sEE6iSj/ZZpRYKorXWscM/qrfW9rwlcGLyymHhnP2AaDbMB1lIA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/node/-/node-2.0.0.tgz", + "integrity": "sha512-Y4hAC2XdGDUdDOCbLDOCA4+aL3NUldjsOWlDL/YwpAxrPhRm1xHd7lZ+mLacvZ9t3PaH28wgNoaLQGrIk1P2pg==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9" @@ -2333,7 +2333,7 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/server": "^2.0.0-beta.5", + "@modelcontextprotocol/server": "^2.0.0", "hono": "^4.11.4" }, "peerDependenciesMeta": { @@ -2388,12 +2388,12 @@ } }, "node_modules/@modelcontextprotocol/server": { - "version": "2.0.0-beta.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0-beta.5.tgz", - "integrity": "sha512-i1E5l75rQKsgY/AKAIspgMBH1vEL7dqiK7tHr0L+raYcb0SWOziqNGJXGIG6NY4AlXDWIKGJQGB7Nqfs3oUi5g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz", + "integrity": "sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/core": "2.0.0-beta.5", + "@modelcontextprotocol/core": "2.0.0", "zod": "^4.2.0" }, "engines": { diff --git a/package.json b/package.json index ac7897c..5e0d3c5 100644 --- a/package.json +++ b/package.json @@ -78,9 +78,9 @@ "author": "Olivier Chafik", "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/core": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", "@types/node": "20.19.27", @@ -111,9 +111,9 @@ "zod": "^4.2.0" }, "peerDependencies": { - "@modelcontextprotocol/client": "2.0.0-beta.5", - "@modelcontextprotocol/core": "2.0.0-beta.5", - "@modelcontextprotocol/server": "2.0.0-beta.5", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/server": "2.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^4.2.0" From 10195ad91851502134930e9b80ec2c04e277a720 Mon Sep 17 00:00:00 2001 From: Quentin Churet <45853629+qchuchu@users.noreply.github.com> Date: Wed, 12 Aug 2026 17:04:54 +0200 Subject: [PATCH 24/45] docs: add Alpic Playground to supported clients (#729) Co-authored-by: Claude Opus 4.8 (1M context) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2fd89ff..77183ab 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ it will guide you through the rest. Postman MCPJam mcp-use + Alpic Playground

> [!NOTE] From 264d9cae448fd07bf5f718d188e5528936796fa0 Mon Sep 17 00:00:00 2001 From: Ken Jo Date: Sat, 11 Jul 2026 21:03:43 +0900 Subject: [PATCH 25/45] fix: add explicit .js extensions to relative imports so declarations resolve under NodeNext/Node16 The emitted .d.ts copy relative specifiers verbatim, and the extensionless form (e.g. `from "./events"`) doesn't resolve under Node16/NodeNext, hiding inherited members such as `addEventListener` (TS2339). Appending `.js` matches the existing SDK-import style and works with both bundler-mode type-checking and Bun.build. Types-only; runtime is unaffected. Fixes #704 --- src/app-bridge.test.ts | 6 +++--- src/app-bridge.ts | 12 ++++++------ src/app.ts | 20 ++++++++++---------- src/message-transport.test.ts | 2 +- src/message-transport.ts | 2 +- src/react/index.tsx | 8 ++++---- src/react/useApp.tsx | 4 ++-- src/react/useAutoResize.ts | 2 +- src/react/useDocumentTheme.ts | 4 ++-- src/react/useHostStyles.ts | 6 +++--- src/server/index.test.ts | 2 +- src/styles.ts | 2 +- 12 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index e538f26..397e12f 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -3,8 +3,8 @@ import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { Server, type ServerCapabilities } from "@modelcontextprotocol/server"; import { z } from "zod/v4"; -import { App } from "./app"; -import { LATEST_PROTOCOL_VERSION } from "./types"; +import { App } from "./app.js"; +import { LATEST_PROTOCOL_VERSION } from "./types.js"; import { AppBridge, buildAllowAttribute, @@ -13,7 +13,7 @@ import { isToolVisibilityAppOnly, McpUiOpenLinkResultSchema, type McpUiHostCapabilities, -} from "./app-bridge"; +} from "./app-bridge.js"; /** Wait for pending microtasks to complete */ const flush = () => new Promise((resolve) => setTimeout(resolve, 0)); diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 59ea586..8e48677 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -34,7 +34,7 @@ import { EmptyResultSchema, LoggingMessageNotificationSchema, } from "@modelcontextprotocol/core"; -import { EventDispatcher } from "./events"; +import { EventDispatcher } from "./events.js"; import type { ZodLiteral, ZodObject, ZodType } from "zod/v4"; type MethodSchema = ZodObject<{ @@ -87,11 +87,11 @@ import { McpUiRequestDisplayModeResultSchema, McpUiResourcePermissions, McpUiToolMeta, -} from "./types"; -export * from "./types"; -export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./constants"; -import { RESOURCE_URI_META_KEY } from "./constants"; -export { PostMessageTransport } from "./message-transport"; +} from "./types.js"; +export * from "./types.js"; +export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./constants.js"; +import { RESOURCE_URI_META_KEY } from "./constants.js"; +export { PostMessageTransport } from "./message-transport.js"; /** * Extract UI resource URI from tool metadata. diff --git a/src/app.ts b/src/app.ts index 34ef284..3fe84d6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -22,10 +22,10 @@ import { type Transport, } from "@modelcontextprotocol/client"; import { EmptyResultSchema } from "@modelcontextprotocol/core"; -export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants"; -import { EventDispatcher } from "./events"; -export { EventDispatcher } from "./events"; -import { PostMessageTransport } from "./message-transport"; +export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants.js"; +import { EventDispatcher } from "./events.js"; +export { EventDispatcher } from "./events.js"; +import { PostMessageTransport } from "./message-transport.js"; import { LATEST_PROTOCOL_VERSION, McpUiAppCapabilities, @@ -59,12 +59,12 @@ import { McpUiToolResultNotificationSchema, McpUiRequestDisplayModeRequest, McpUiRequestDisplayModeResultSchema, -} from "./types"; +} from "./types.js"; import { StandardSchemaV1, standardSchemaToJsonSchema, validateStandardSchema, -} from "./standard-schema"; +} from "./standard-schema.js"; import { z, type ZodLiteral, type ZodObject, type ZodType } from "zod/v4"; type MethodSchema = ZodObject<{ @@ -92,16 +92,16 @@ function mergeAppCapabilities( export type { StandardSchemaV1, StandardSchemaWithJSON, -} from "./standard-schema"; +} from "./standard-schema.js"; -export { PostMessageTransport } from "./message-transport"; -export * from "./types"; +export { PostMessageTransport } from "./message-transport.js"; +export * from "./types.js"; export { applyHostStyleVariables, applyHostFonts, getDocumentTheme, applyDocumentTheme, -} from "./styles"; +} from "./styles.js"; /** * Metadata key for associating a UI resource URI with a tool. diff --git a/src/message-transport.test.ts b/src/message-transport.test.ts index 2a115f3..e2518cc 100644 --- a/src/message-transport.test.ts +++ b/src/message-transport.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"; import type { JSONRPCMessage } from "@modelcontextprotocol/client"; -import { PostMessageTransport } from "./message-transport"; +import { PostMessageTransport } from "./message-transport.js"; /** * Minimal `window` stub for bun's DOM-less test environment. diff --git a/src/message-transport.ts b/src/message-transport.ts index 9483540..afc85e9 100644 --- a/src/message-transport.ts +++ b/src/message-transport.ts @@ -5,7 +5,7 @@ import type { Transport, TransportSendOptions, } from "@modelcontextprotocol/client"; -import { TOOL_INPUT_PARTIAL_METHOD } from "./spec.types"; +import { TOOL_INPUT_PARTIAL_METHOD } from "./spec.types.js"; /** * JSON-RPC transport using `window.postMessage` for iframe↔parent communication. diff --git a/src/react/index.tsx b/src/react/index.tsx index e76e197..9693fb1 100644 --- a/src/react/index.tsx +++ b/src/react/index.tsx @@ -31,7 +31,7 @@ * } * ``` */ -export * from "./useApp"; -export * from "./useAutoResize"; -export * from "./useDocumentTheme"; -export * from "./useHostStyles"; +export * from "./useApp.js"; +export * from "./useAutoResize.js"; +export * from "./useDocumentTheme.js"; +export * from "./useHostStyles.js"; diff --git a/src/react/useApp.tsx b/src/react/useApp.tsx index 0e64a74..a8e1a09 100644 --- a/src/react/useApp.tsx +++ b/src/react/useApp.tsx @@ -5,8 +5,8 @@ import { AppOptions, McpUiAppCapabilities, PostMessageTransport, -} from "../app"; -export * from "../app"; +} from "../app.js"; +export * from "../app.js"; /** * Options for configuring the {@link useApp `useApp`} hook. diff --git a/src/react/useAutoResize.ts b/src/react/useAutoResize.ts index 78217d6..66af07e 100644 --- a/src/react/useAutoResize.ts +++ b/src/react/useAutoResize.ts @@ -1,5 +1,5 @@ import { useEffect, RefObject } from "react"; -import { App } from "../app"; +import { App } from "../app.js"; /** * React hook that automatically reports UI size changes to the host. diff --git a/src/react/useDocumentTheme.ts b/src/react/useDocumentTheme.ts index 2fdfcc2..3dc851e 100644 --- a/src/react/useDocumentTheme.ts +++ b/src/react/useDocumentTheme.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { getDocumentTheme } from "../styles"; -import { McpUiTheme } from "../types"; +import { getDocumentTheme } from "../styles.js"; +import { McpUiTheme } from "../types.js"; /** * React hook that provides the current document theme reactively. diff --git a/src/react/useHostStyles.ts b/src/react/useHostStyles.ts index e380efa..d8376a0 100644 --- a/src/react/useHostStyles.ts +++ b/src/react/useHostStyles.ts @@ -1,11 +1,11 @@ import { useEffect, useRef } from "react"; -import { App } from "../app"; +import { App } from "../app.js"; import { applyDocumentTheme, applyHostFonts, applyHostStyleVariables, -} from "../styles"; -import { McpUiHostContext } from "../types"; +} from "../styles.js"; +import { McpUiHostContext } from "../types.js"; /** * React hook that applies host style variables and theme as CSS custom properties. diff --git a/src/server/index.test.ts b/src/server/index.test.ts index 5de341f..389cde1 100644 --- a/src/server/index.test.ts +++ b/src/server/index.test.ts @@ -6,7 +6,7 @@ import { RESOURCE_MIME_TYPE, getUiCapability, EXTENSION_ID, -} from "./index"; +} from "./index.js"; import type { McpServer } from "@modelcontextprotocol/server"; describe("registerAppTool", () => { diff --git a/src/styles.ts b/src/styles.ts index ad900e2..895073b 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -1,4 +1,4 @@ -import { McpUiStyles, McpUiTheme } from "./types"; +import { McpUiStyles, McpUiTheme } from "./types.js"; /** * Get the current document theme from the root HTML element. From 90123733bbc7854a543a05db64c1a6a9b8ee0613 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 11:49:33 +0100 Subject: [PATCH 26/45] test: regenerate lockfile (re-add nested @types/node entries) --- package-lock.json | 401 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) diff --git a/package-lock.json b/package-lock.json index 49b1315..ca37f4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-host/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-host/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-preact": { "name": "@modelcontextprotocol/server-basic-preact", "version": "1.7.5", @@ -129,6 +146,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-preact/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-preact/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-react": { "name": "@modelcontextprotocol/server-basic-react", "version": "1.7.5", @@ -162,6 +196,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-react/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-react/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-solid": { "name": "@modelcontextprotocol/server-basic-solid", "version": "1.7.5", @@ -192,6 +243,23 @@ "vite-plugin-solid": "^2.11.12" } }, + "examples/basic-server-solid/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-solid/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-svelte": { "name": "@modelcontextprotocol/server-basic-svelte", "version": "1.7.5", @@ -222,6 +290,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-svelte/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-svelte/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-vanillajs": { "name": "@modelcontextprotocol/server-basic-vanillajs", "version": "1.7.5", @@ -250,6 +335,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-vanillajs/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-vanillajs/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/basic-server-vue": { "name": "@modelcontextprotocol/server-basic-vue", "version": "1.7.5", @@ -280,6 +382,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/basic-server-vue/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/basic-server-vue/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/budget-allocator-server": { "name": "@modelcontextprotocol/server-budget-allocator", "version": "1.7.5", @@ -308,6 +427,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/budget-allocator-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/budget-allocator-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/cohort-heatmap-server": { "name": "@modelcontextprotocol/server-cohort-heatmap", "version": "1.7.5", @@ -340,6 +476,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/cohort-heatmap-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/cohort-heatmap-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/customer-segmentation-server": { "name": "@modelcontextprotocol/server-customer-segmentation", "version": "1.7.5", @@ -368,6 +521,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/customer-segmentation-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/customer-segmentation-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/debug-server": { "name": "@modelcontextprotocol/server-debug", "version": "1.7.5", @@ -395,6 +565,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/debug-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/debug-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/integration-server": { "version": "1.7.5", "dependencies": { @@ -425,6 +612,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/integration-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/integration-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/lazy-auth-server": { "name": "@modelcontextprotocol/server-lazy-auth", "version": "1.7.5", @@ -452,6 +656,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/lazy-auth-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/lazy-auth-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/map-server": { "name": "@modelcontextprotocol/server-map", "version": "1.7.5", @@ -480,6 +701,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/map-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/map-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/pdf-server": { "name": "@modelcontextprotocol/server-pdf", "version": "1.7.5", @@ -510,6 +748,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/pdf-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/pdf-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", "version": "1.7.5", @@ -542,6 +797,16 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/quickstart/node_modules/@types/node": { + "version": "22.19.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.5.tgz", + "integrity": "sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", "version": "1.7.5", @@ -583,6 +848,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/scenario-modeler-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/scenario-modeler-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/shadertoy-server": { "name": "@modelcontextprotocol/server-shadertoy", "version": "1.7.5", @@ -610,6 +892,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/shadertoy-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/shadertoy-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/sheet-music-server": { "name": "@modelcontextprotocol/server-sheet-music", "version": "1.7.5", @@ -638,6 +937,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/sheet-music-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/sheet-music-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/system-monitor-server": { "name": "@modelcontextprotocol/server-system-monitor", "version": "1.7.5", @@ -667,6 +983,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/system-monitor-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/system-monitor-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/threejs-server": { "name": "@modelcontextprotocol/server-threejs", "version": "1.7.5", @@ -702,6 +1035,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/threejs-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/threejs-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/transcript-server": { "name": "@modelcontextprotocol/server-transcript", "version": "1.7.5", @@ -730,6 +1080,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/transcript-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/transcript-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/video-resource-server": { "name": "@modelcontextprotocol/server-video-resource", "version": "1.7.5", @@ -758,6 +1125,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/video-resource-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/video-resource-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "examples/wiki-explorer-server": { "name": "@modelcontextprotocol/server-wiki-explorer", "version": "1.7.5", @@ -788,6 +1172,23 @@ "vite-plugin-singlefile": "^2.3.0" } }, + "examples/wiki-explorer-server/node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "examples/wiki-explorer-server/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", From f28f5ddb3c3d451923d25e5591841ba76ef21df0 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:20:52 +0100 Subject: [PATCH 27/45] Harden dependency isolation check and run it in CI npm pack runs the prepare script for directory specs even with --ignore-scripts and its output can pollute stdout, so locate the packed tarball on disk instead of parsing --json output. Run npm with a minimal environment so caller npm config cannot leak into the synthetic consumers, and require exact versions for the consumers' dependencies. Add a CI job that runs the check on Node 22. --- .github/workflows/ci.yml | 15 +++++++ scripts/check-dependency-isolation.mjs | 60 ++++++++++++++++++-------- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb41938..756f259 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,6 +114,21 @@ jobs: grep -q "Ready" /tmp/mcpb-smoke.log working-directory: examples/pdf-server + dependency-isolation: + name: Dependency isolation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version: "22" + cache: npm + + - run: npm ci + + - run: npm run test:dependency-isolation + e2e: runs-on: ubuntu-latest steps: diff --git a/scripts/check-dependency-isolation.mjs b/scripts/check-dependency-isolation.mjs index 2ec28ce..2dfe577 100644 --- a/scripts/check-dependency-isolation.mjs +++ b/scripts/check-dependency-isolation.mjs @@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process"; import { mkdtempSync, mkdirSync, + readdirSync, readFileSync, rmSync, writeFileSync, @@ -25,33 +26,56 @@ for (const role of [client, server]) { } } +/** + * Exact version for a synthetic consumer dependency. Read from + * devDependencies so the consumers exercise the same SDK version the + * repository tests against; the check would silently drift if a range were + * allowed here. + */ +function exactDevDependency(name) { + const version = packageJson.devDependencies?.[name]; + if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(version ?? "")) { + throw new Error( + `devDependencies["${name}"] must be an exact version, got ${JSON.stringify(version)}`, + ); + } + return version; +} + const temporaryRoot = mkdtempSync(join(tmpdir(), "ext-apps-role-peers-")); try { + // Minimal environment: nothing inherited from the caller's npm config + // (registry overrides, auth tokens, npm_config_* set by an outer `npm run`) + // can leak into the synthetic consumers. const npmEnvironment = { - ...process.env, + PATH: process.env.PATH ?? process.env.Path, + HOME: process.env.HOME ?? process.env.USERPROFILE, npm_config_cache: join(temporaryRoot, "npm-cache"), }; - const packOutput = JSON.parse( - execFileSync( - "npm", - [ - "pack", - "--ignore-scripts", - "--json", - "--pack-destination", - temporaryRoot, - ], - { cwd: root, encoding: "utf8", env: npmEnvironment }, - ), + const packDestination = join(temporaryRoot, "pack"); + mkdirSync(packDestination); + // `npm pack` runs the package's `prepare` script even with --ignore-scripts + // (pacote's directory fetcher), and its output can pollute stdout, so do not + // rely on `--json`: locate the tarball on disk instead. + execFileSync( + "npm", + ["pack", "--ignore-scripts", "--pack-destination", packDestination], + { cwd: root, stdio: "pipe", env: npmEnvironment }, ); - const tarball = join(temporaryRoot, packOutput[0].filename); + const tarballs = readdirSync(packDestination).filter((name) => + name.endsWith(".tgz"), + ); + if (tarballs.length !== 1) { + throw new Error(`expected exactly one tarball, found ${tarballs}`); + } + const tarball = join(packDestination, tarballs[0]); const consumers = [ { name: "app-only", dependencies: { - "@types/node": packageJson.devDependencies["@types/node"], - [client]: packageJson.devDependencies[client], + "@types/node": exactDevDependency("@types/node"), + [client]: exactDevDependency(client), "@modelcontextprotocol/ext-apps": `file:${tarball}`, }, absent: server, @@ -61,9 +85,9 @@ try { { name: "server-only", dependencies: { - "@types/node": packageJson.devDependencies["@types/node"], + "@types/node": exactDevDependency("@types/node"), "@modelcontextprotocol/ext-apps": `file:${tarball}`, - [server]: packageJson.devDependencies[server], + [server]: exactDevDependency(server), }, absent: client, entry: From 50eb30ec8dff82b0b505ec5351b1bbf52cd9d2fc Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:24:13 +0100 Subject: [PATCH 28/45] Bump to 2.0.0 and widen SDK peer ranges to ^2.0.0 The client, core and server packages become ^2.0.0 peers and client is now a required peer (App and AppBridge extend its Protocol class); server stays optional for View-only consumers. Examples move to zod ^4.2.0 to match the library peer range. The root package is not yet published at 2.0.0, so the lockfile now links the examples' @modelcontextprotocol/ext-apps dependency to the repository root instead of a registry copy. This drops the stale 1.x tarball (and its sdk 1.x peer) from the lockfile and makes scripts/link-self.mjs a no-op. --- examples/basic-host/package.json | 6 +- examples/basic-server-preact/package.json | 6 +- examples/basic-server-react/package.json | 6 +- examples/basic-server-solid/package.json | 6 +- examples/basic-server-svelte/package.json | 6 +- examples/basic-server-vanillajs/package.json | 6 +- examples/basic-server-vue/package.json | 6 +- examples/budget-allocator-server/package.json | 6 +- examples/cohort-heatmap-server/package.json | 6 +- .../customer-segmentation-server/package.json | 6 +- examples/debug-server/package.json | 6 +- examples/integration-server/package.json | 6 +- examples/lazy-auth-server/package.json | 6 +- examples/map-server/package.json | 6 +- examples/pdf-server/package.json | 6 +- examples/qr-server/package.json | 4 +- examples/quickstart/package.json | 6 +- examples/say-server/package.json | 4 +- examples/scenario-modeler-server/package.json | 6 +- examples/shadertoy-server/package.json | 6 +- examples/sheet-music-server/package.json | 6 +- examples/system-monitor-server/package.json | 6 +- examples/threejs-server/package.json | 6 +- examples/transcript-server/package.json | 6 +- examples/video-resource-server/package.json | 6 +- examples/wiki-explorer-server/package.json | 6 +- package-lock.json | 357 ++++-------------- package.json | 11 +- 28 files changed, 163 insertions(+), 357 deletions(-) diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index 06d1a05..cefe9d5 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -1,7 +1,7 @@ { "homepage": "https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host", "name": "@modelcontextprotocol/ext-apps-basic-host", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "scripts": { "build": "tsc --noEmit && concurrently \"cross-env INPUT=index.html vite build\" \"cross-env INPUT=sandbox.html vite build\"", @@ -11,11 +11,11 @@ "dev": "cross-env NODE_ENV=development concurrently \"npm run watch\" \"npm run serve\"" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/express": "^5.0.0", diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index 82a30bb..c88a306 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-preact", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Basic MCP App Server example using Preact", "repository": { @@ -25,14 +25,14 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@preact/preset-vite": "^2.0.0", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index b2b9540..07a9d3b 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-react", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Basic MCP App Server example using React", "repository": { @@ -35,15 +35,15 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index 2e3bc79..a459ebc 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-solid", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Basic MCP App Server example using Solid", "repository": { @@ -25,14 +25,14 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index cd7fb41..3e0279f 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-svelte", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Basic MCP App Server example using Svelte", "repository": { @@ -25,14 +25,14 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 50f0f42..1404b65 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-vanillajs", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Basic MCP App Server example using vanilla JavaScript", "repository": { @@ -25,13 +25,13 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index 2ff3e0a..32083c6 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-basic-vue", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Basic MCP App Server example using Vue", "repository": { @@ -25,14 +25,14 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 9208e91..e44a147 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-budget-allocator", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Budget allocator MCP App Server with interactive visualization", "repository": { @@ -26,14 +26,14 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index eb58415..de20f13 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-cohort-heatmap", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Cohort heatmap MCP App Server for retention analysis", "repository": { @@ -26,15 +26,15 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index f6999ee..643db17 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-customer-segmentation", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Customer segmentation MCP App Server with filtering", "repository": { @@ -26,14 +26,14 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index 33cede4..5be9291 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-debug", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Debug MCP App Server for testing all SDK capabilities", "repository": { @@ -34,11 +34,11 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json index 18f22e6..7e04f4a 100644 --- a/examples/integration-server/package.json +++ b/examples/integration-server/package.json @@ -1,6 +1,6 @@ { "name": "integration-server", - "version": "1.7.5", + "version": "2.0.0", "private": true, "type": "module", "scripts": { @@ -16,15 +16,15 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index 6e7aaa7..b12b29b 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-lazy-auth", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP App example demonstrating lazy (on-demand) OAuth: public tools work unauthenticated, protected tools return 401 + WWW-Authenticate so the host runs the OAuth flow only when needed", "repository": { @@ -22,13 +22,13 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "jose": "^6.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/map-server/package.json b/examples/map-server/package.json index a59445e..dc6a37f 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-map", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP App Server example with CesiumJS 3D globe and geocoding", "repository": { @@ -27,13 +27,13 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index 8c2c2ee..ae0870a 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-pdf", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP server for loading and extracting text from PDF files with chunked pagination and interactive viewer", "repository": { @@ -27,14 +27,14 @@ "dependencies": { "@cantoo/pdf-lib": "^2.6.5", "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/qr-server/package.json b/examples/qr-server/package.json index e1b849b..425728e 100644 --- a/examples/qr-server/package.json +++ b/examples/qr-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-qr", - "version": "1.7.5", + "version": "2.0.0", "private": true, "scripts": { "start": "uv run server.py", @@ -8,6 +8,6 @@ "build": "echo 'No build step needed for Python server'" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0" + "@modelcontextprotocol/ext-apps": "^2.0.0" } } diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index 6874b49..eecb080 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/quickstart", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "private": true, "description": "Quickstart MCP App Server example", @@ -15,13 +15,13 @@ "start": "concurrently --raw \"cross-env NODE_ENV=development INPUT=mcp-app.html vite build --watch\" \"tsx watch main.ts\"" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/say-server/package.json b/examples/say-server/package.json index 5f0f738..cff7024 100644 --- a/examples/say-server/package.json +++ b/examples/say-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-say", - "version": "1.7.5", + "version": "2.0.0", "private": true, "description": "Streaming TTS MCP App Server with karaoke-style text highlighting", "repository": { @@ -15,6 +15,6 @@ "build": "echo 'No build step needed for Python server'" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0" + "@modelcontextprotocol/ext-apps": "^2.0.0" } } diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index 76d692a..d82424d 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-scenario-modeler", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Financial scenario modeling MCP App Server", "repository": { @@ -26,8 +26,8 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", @@ -35,7 +35,7 @@ "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index 57edf08..3e16a92 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-shadertoy", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP App Server example for rendering ShaderToy-compatible GLSL shaders", "repository": { @@ -24,13 +24,13 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index 55c2870..1b233bb 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-sheet-music", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP App Server for rendering and playing sheet music from ABC notation", "repository": { @@ -24,14 +24,14 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index b0c2f20..f3a5763 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-system-monitor", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "System monitor MCP App Server with real-time stats", "repository": { @@ -26,15 +26,15 @@ "serve": "bun --watch main.ts" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", "systeminformation": "^5.31.6", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index 40d1c25..f8df10f 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-threejs", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Three.js 3D visualization MCP App Server", "repository": { @@ -27,8 +27,8 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", @@ -36,7 +36,7 @@ "react": "^19.2.0", "react-dom": "^19.2.0", "three": "^0.181.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index e7c5dba..405f85f 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-transcript", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP App Server for live speech transcription", "repository": { @@ -24,13 +24,13 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index 199a373..dea8fcb 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-video-resource", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "MCP App Server demonstrating video resources served as base64 blobs", "repository": { @@ -25,13 +25,13 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 090a851..0831670 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server-wiki-explorer", - "version": "1.7.5", + "version": "2.0.0", "type": "module", "description": "Wikipedia link explorer MCP App Server with graph visualization", "repository": { @@ -27,14 +27,14 @@ }, "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", "@modelcontextprotocol/express": "2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", diff --git a/package-lock.json b/package-lock.json index ca37f4b..f63b3d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@modelcontextprotocol/ext-apps", - "version": "1.7.5", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@modelcontextprotocol/ext-apps", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "workspaces": [ "examples/*" @@ -52,17 +52,14 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/core": "2.0.0", - "@modelcontextprotocol/server": "2.0.0", + "@modelcontextprotocol/client": "^2.0.0", + "@modelcontextprotocol/core": "^2.0.0", + "@modelcontextprotocol/server": "^2.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^4.2.0" }, "peerDependenciesMeta": { - "@modelcontextprotocol/client": { - "optional": true - }, "@modelcontextprotocol/server": { "optional": true }, @@ -76,13 +73,13 @@ }, "examples/basic-host": { "name": "@modelcontextprotocol/ext-apps-basic-host", - "version": "1.7.5", + "version": "2.0.0", "dependencies": { "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/express": "^5.0.0", @@ -118,18 +115,18 @@ }, "examples/basic-server-preact": { "name": "@modelcontextprotocol/server-basic-preact", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "preact": "^10.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-basic-preact": "dist/index.js" @@ -165,19 +162,19 @@ }, "examples/basic-server-react": { "name": "@modelcontextprotocol/server-basic-react", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-basic-react": "dist/index.js" @@ -215,18 +212,18 @@ }, "examples/basic-server-solid": { "name": "@modelcontextprotocol/server-basic-solid", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "solid-js": "1.9.10", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-basic-solid": "dist/index.js" @@ -262,18 +259,18 @@ }, "examples/basic-server-svelte": { "name": "@modelcontextprotocol/server-basic-svelte", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "svelte": "^5.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-basic-svelte": "dist/index.js" @@ -309,17 +306,17 @@ }, "examples/basic-server-vanillajs": { "name": "@modelcontextprotocol/server-basic-vanillajs", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-basic-vanillajs": "dist/index.js" @@ -354,18 +351,18 @@ }, "examples/basic-server-vue": { "name": "@modelcontextprotocol/server-basic-vue", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "vue": "^3.5.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-basic-vue": "dist/index.js" @@ -401,17 +398,17 @@ }, "examples/budget-allocator-server": { "name": "@modelcontextprotocol/server-budget-allocator", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-budget-allocator-server": "dist/index.js" @@ -446,18 +443,18 @@ }, "examples/cohort-heatmap-server": { "name": "@modelcontextprotocol/server-cohort-heatmap", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-cohort-heatmap-server": "dist/index.js" @@ -495,17 +492,17 @@ }, "examples/customer-segmentation-server": { "name": "@modelcontextprotocol/server-customer-segmentation", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-customer-segmentation-server": "dist/index.js" @@ -540,14 +537,14 @@ }, "examples/debug-server": { "name": "@modelcontextprotocol/server-debug", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-debug": "dist/index.js" @@ -583,18 +580,18 @@ "license": "MIT" }, "examples/integration-server": { - "version": "1.7.5", + "version": "2.0.0", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-integration-server": "dist/index.js" @@ -631,16 +628,16 @@ }, "examples/lazy-auth-server": { "name": "@modelcontextprotocol/server-lazy-auth", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "jose": "^6.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-server-lazy-auth": "dist/index.js" @@ -675,17 +672,17 @@ }, "examples/map-server": { "name": "@modelcontextprotocol/server-map", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-map-server": "dist/index.js" @@ -720,19 +717,19 @@ }, "examples/pdf-server": { "name": "@modelcontextprotocol/server-pdf", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@cantoo/pdf-lib": "^2.6.5", "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", "pdfjs-dist": "^5.0.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-pdf-server": "dist/index.js" @@ -767,23 +764,23 @@ }, "examples/qr-server": { "name": "@modelcontextprotocol/server-qr", - "version": "1.7.5", + "version": "2.0.0", "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0" + "@modelcontextprotocol/ext-apps": "^2.0.0" } }, "examples/quickstart": { "name": "@modelcontextprotocol/quickstart", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "devDependencies": { "@types/cors": "^2.8.19", @@ -809,19 +806,19 @@ }, "examples/say-server": { "name": "@modelcontextprotocol/server-say", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.7.0" + "@modelcontextprotocol/ext-apps": "^2.0.0" } }, "examples/scenario-modeler-server": { "name": "@modelcontextprotocol/server-scenario-modeler", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", @@ -829,7 +826,7 @@ "express": "^5.1.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-scenario-modeler-server": "dist/index.js" @@ -867,16 +864,16 @@ }, "examples/shadertoy-server": { "name": "@modelcontextprotocol/server-shadertoy", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-shadertoy-server": "dist/index.js" @@ -911,17 +908,17 @@ }, "examples/sheet-music-server": { "name": "@modelcontextprotocol/server-sheet-music", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "abcjs": "^6.4.4", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-sheet-music-server": "dist/index.js" @@ -956,18 +953,18 @@ }, "examples/system-monitor-server": { "name": "@modelcontextprotocol/server-system-monitor", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "chart.js": "^4.4.0", "cors": "^2.8.5", "express": "^5.1.0", "systeminformation": "^5.31.6", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-system-monitor-server": "dist/index.js" @@ -1002,12 +999,12 @@ }, "examples/threejs-server": { "name": "@modelcontextprotocol/server-threejs", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", @@ -1015,7 +1012,7 @@ "react": "^19.2.0", "react-dom": "^19.2.0", "three": "^0.181.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-threejs-server": "dist/index.js" @@ -1054,16 +1051,16 @@ }, "examples/transcript-server": { "name": "@modelcontextprotocol/server-transcript", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-transcript-server": "dist/index.js" @@ -1099,17 +1096,17 @@ }, "examples/video-resource-server": { "name": "@modelcontextprotocol/server-video-resource", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-video-resource-server": "dist/index.js" @@ -1144,18 +1141,18 @@ }, "examples/wiki-explorer-server": { "name": "@modelcontextprotocol/server-wiki-explorer", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/express": "2.0.0", - "@modelcontextprotocol/ext-apps": "^1.7.0", + "@modelcontextprotocol/ext-apps": "^2.0.0", "@modelcontextprotocol/node": "2.0.0", "@modelcontextprotocol/server": "2.0.0", "cheerio": "^1.0.0", "cors": "^2.8.5", "express": "^5.1.0", - "zod": "^4.1.13" + "zod": "^4.2.0" }, "bin": { "mcp-wiki-explorer-server": "dist/index.js" @@ -2727,33 +2724,8 @@ } }, "node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.1.tgz", - "integrity": "sha512-J3WdG1A4JSSKnSWKyU+895dBVYBV2Utgtf7fUsUK45mlkETm53a/1DR6Pm3hUGKqLLQthZLmpxOg8VPzJi/lyg==", - "license": "MIT", - "workspaces": [ - "examples/*" - ], - "dependencies": { - "@standard-schema/spec": "^1.1.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } + "resolved": "", + "link": true }, "node_modules/@modelcontextprotocol/ext-apps-basic-host": { "resolved": "examples/basic-host", @@ -2784,47 +2756,6 @@ "resolved": "examples/quickstart", "link": true }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, "node_modules/@modelcontextprotocol/server": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz", @@ -4444,41 +4375,6 @@ "node": ">=0.4.0" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -6041,49 +5937,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express-rate-limit": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", - "license": "MIT", - "peer": true, - "dependencies": { - "ip-address": "^10.2.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT", - "peer": true - }, - "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -6546,16 +6399,6 @@ "node": ">=12" } }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 12" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6743,20 +6586,6 @@ "node": ">=6" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause", - "peer": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -7749,16 +7578,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -9897,16 +9716,6 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "license": "ISC", - "peer": true, - "peerDependencies": { - "zod": "^3.25 || ^4" - } } } } diff --git a/package.json b/package.json index fc887fa..da82a66 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "url": "https://github.com/modelcontextprotocol/ext-apps" }, "homepage": "https://github.com/modelcontextprotocol/ext-apps", - "version": "1.7.5", + "version": "2.0.0", "license": "MIT", "description": "MCP Apps SDK — Enable MCP servers to display interactive user interfaces in conversational clients.", "type": "module", @@ -111,17 +111,14 @@ "zod": "^4.2.0" }, "peerDependencies": { - "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/core": "2.0.0", - "@modelcontextprotocol/server": "2.0.0", + "@modelcontextprotocol/client": "^2.0.0", + "@modelcontextprotocol/core": "^2.0.0", + "@modelcontextprotocol/server": "^2.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "zod": "^4.2.0" }, "peerDependenciesMeta": { - "@modelcontextprotocol/client": { - "optional": true - }, "@modelcontextprotocol/server": { "optional": true }, From 040942b3af064bf76b705021e3472b6593ae839e Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:26:00 +0100 Subject: [PATCH 29/45] Import shared wire types from @modelcontextprotocol/client spec.types.ts previously inferred CallToolResult, ContentBlock and friends from core's Zod schemas via a local core-types module so that the type surface stayed free of a client package edge. Now that client is a required peer, take the types from it directly and delete the indirection. Generated schemas are unchanged. --- scripts/generate-schemas.ts | 8 +++++--- src/core-types.ts | 20 -------------------- src/spec.types.ts | 2 +- 3 files changed, 6 insertions(+), 24 deletions(-) delete mode 100644 src/core-types.ts diff --git a/scripts/generate-schemas.ts b/scripts/generate-schemas.ts index 85225f5..0ff9f04 100644 --- a/scripts/generate-schemas.ts +++ b/scripts/generate-schemas.ts @@ -22,10 +22,12 @@ * * **Problem**: ts-to-zod cannot resolve types imported from external packages. * When it encounters types like `ContentBlock`, `CallToolResult`, `Implementation`, - * `RequestId`, and `Tool` from `@modelcontextprotocol/core`, it generates `z.any()` - * as a placeholder. + * `RequestId`, and `Tool` (imported by spec.types.ts from + * `@modelcontextprotocol/client`), it generates `z.any()` as a placeholder. * - * **Solution**: Import the schemas from MCP SDK and remove the z.any() placeholders. + * **Solution**: Import the corresponding Zod schemas from + * `@modelcontextprotocol/core` (the role-neutral package both client and + * server re-export) and remove the z.any() placeholders. * * ### 3. Index Signatures (`z.record().and()` → `z.object().passthrough()`) * diff --git a/src/core-types.ts b/src/core-types.ts deleted file mode 100644 index 19392d2..0000000 --- a/src/core-types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - CallToolResultSchema, - ContentBlockSchema, - EmbeddedResourceSchema, - ImplementationSchema, - RequestIdSchema, - ResourceLinkSchema, - ToolSchema, -} from "@modelcontextprotocol/core"; -import type { z } from "zod/v4"; - -// Infer shared wire types from the public role-neutral schemas so declarations -// used by every entrypoint do not acquire a client or server package edge. -export type CallToolResult = z.infer; -export type ContentBlock = z.infer; -export type EmbeddedResource = z.infer; -export type Implementation = z.infer; -export type RequestId = z.infer; -export type ResourceLink = z.infer; -export type Tool = z.infer; diff --git a/src/spec.types.ts b/src/spec.types.ts index 5d3fda6..0c45396 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -18,7 +18,7 @@ import type { RequestId, ResourceLink, Tool, -} from "./core-types"; +} from "@modelcontextprotocol/client"; /** * Current protocol version supported by this SDK. From d6ebcca9a0f4536779666b2223916d64f3700a64 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:27:11 +0100 Subject: [PATCH 30/45] Accept raw zod shapes in registerAppTool as a deprecated overload SDK v2 registerTool takes Standard JSON Schema objects (z.object(...)) and keeps a deprecated overload for the v1 raw-shape form. Mirror that here so existing servers passing { field: z.string() } keep compiling; the call is forwarded to the SDK, which auto-wraps the shape. Tests exercise both forms end to end through a real McpServer and Client. --- src/server/index.test.ts | 121 ++++++++++++++++++++++++++++++++++++++- src/server/index.ts | 78 ++++++++++++++++++++++++- 2 files changed, 195 insertions(+), 4 deletions(-) diff --git a/src/server/index.test.ts b/src/server/index.test.ts index 389cde1..489d0b0 100644 --- a/src/server/index.test.ts +++ b/src/server/index.test.ts @@ -7,7 +7,13 @@ import { getUiCapability, EXTENSION_ID, } from "./index.js"; -import type { McpServer } from "@modelcontextprotocol/server"; +import { + InMemoryTransport, + McpServer as RealMcpServer, + type McpServer, +} from "@modelcontextprotocol/server"; +import { Client } from "@modelcontextprotocol/client"; +import { z } from "zod/v4"; describe("registerAppTool", () => { it("should pass through config to server.registerTool", () => { @@ -197,6 +203,119 @@ describe("registerAppTool", () => { }); }); +describe("registerAppTool schema forms", () => { + /** Registers tools on a fresh server, then connects a client to it. */ + async function connect(register: (server: RealMcpServer) => void) { + const server = new RealMcpServer({ name: "test", version: "0.0.0" }); + register(server); + const client = new Client({ name: "client", version: "0.0.0" }); + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + await server.connect(serverTransport); + await client.connect(clientTransport); + return client; + } + + it("accepts a z.object() input schema and validates arguments", async () => { + const client = await connect((server) => + registerAppTool( + server, + "greet", + { + inputSchema: z.object({ name: z.string() }), + _meta: { ui: { resourceUri: "ui://greet/view.html" } }, + }, + async ({ name }) => ({ + content: [{ type: "text", text: `hello ${name}` }], + }), + ), + ); + + const { tools } = await client.listTools(); + expect(tools).toHaveLength(1); + expect(tools[0].inputSchema).toMatchObject({ + type: "object", + properties: { name: { type: "string" } }, + }); + expect(tools[0]._meta).toMatchObject({ + ui: { resourceUri: "ui://greet/view.html" }, + [RESOURCE_URI_META_KEY]: "ui://greet/view.html", + }); + + const result = await client.callTool({ + name: "greet", + arguments: { name: "world" }, + }); + expect(result.content).toEqual([{ type: "text", text: "hello world" }]); + + const invalid = await client.callTool({ + name: "greet", + arguments: { name: 42 }, + }); + expect(invalid.isError).toBe(true); + }); + + it("accepts a raw zod shape (deprecated form) and wraps it with z.object()", async () => { + const client = await connect((server) => + registerAppTool( + server, + "greet", + { + inputSchema: { name: z.string() }, + outputSchema: { greeting: z.string() }, + _meta: { ui: { resourceUri: "ui://greet/view.html" } }, + }, + async ({ name }) => ({ + content: [{ type: "text", text: `hello ${name}` }], + structuredContent: { greeting: `hello ${name}` }, + }), + ), + ); + + const { tools } = await client.listTools(); + expect(tools[0].inputSchema).toMatchObject({ + type: "object", + properties: { name: { type: "string" } }, + }); + expect(tools[0].outputSchema).toMatchObject({ + type: "object", + properties: { greeting: { type: "string" } }, + }); + + const result = await client.callTool({ + name: "greet", + arguments: { name: "world" }, + }); + expect(result.structuredContent).toEqual({ greeting: "hello world" }); + + const invalid = await client.callTool({ + name: "greet", + arguments: { name: 42 }, + }); + expect(invalid.isError).toBe(true); + }); + + it("accepts the raw-shape form without an input schema", async () => { + const client = await connect((server) => + registerAppTool( + server, + "ping", + { + outputSchema: { ok: z.boolean() }, + _meta: { ui: { resourceUri: "ui://ping/view.html" } }, + }, + async () => ({ + content: [{ type: "text", text: "pong" }], + structuredContent: { ok: true }, + }), + ), + ); + + const result = await client.callTool({ name: "ping", arguments: {} }); + expect(result.structuredContent).toEqual({ ok: true }); + }); +}); + describe("registerAppResource", () => { it("should register a resource with default MIME type", () => { let capturedName: string | undefined; diff --git a/src/server/index.ts b/src/server/index.ts index dc9a1f3..5fad055 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -39,31 +39,69 @@ import { } from "../spec.types.js"; import { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "../constants.js"; import type { + CallToolResult, ClientCapabilities, + InputRequiredResult, McpServer, ReadResourceResult, RegisteredTool, ResourceMetadata, + ServerContext, StandardSchemaWithJSON, ToolAnnotations, ToolCallback, ReadResourceCallback as _ReadResourceCallback, RegisteredResource, } from "@modelcontextprotocol/server"; +import type { z } from "zod/v4"; // Re-exports for convenience export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE }; export type { ResourceMetadata, ToolCallback }; +/** + * A plain `{ field: z.string() }` record accepted by the deprecated raw-shape + * form of {@link registerAppTool `registerAppTool`}; the SDK auto-wraps it with `z.object()`. + * + * @deprecated Wrap with `z.object({...})` instead. + */ +export type ZodRawShape = Record; + +/** + * {@link ToolCallback `ToolCallback`} variant used when `inputSchema` is a {@link ZodRawShape `ZodRawShape`}. + * Mirrors the callback type of the SDK's deprecated `registerTool` overload. + * + * @deprecated Wrap with `z.object({...})` instead and use {@link ToolCallback `ToolCallback`}. + */ +export type LegacyToolCallback = + Args extends ZodRawShape + ? ( + args: z.infer>, + ctx: ServerContext, + ) => + | CallToolResult + | InputRequiredResult + | Promise + : ( + ctx: ServerContext, + ) => + | CallToolResult + | InputRequiredResult + | Promise; + /** * Base tool configuration matching the standard MCP server tool options. * Extended by {@link McpUiAppToolConfig `McpUiAppToolConfig`} to add UI metadata requirements. + * + * `inputSchema`/`outputSchema` accept any Standard JSON Schema (zod v4, ArkType, + * Valibot, ...). A raw zod shape (`{ field: z.string() }`) is still accepted for + * backward compatibility but deprecated. */ export interface ToolConfig { title?: string; description?: string; - inputSchema?: StandardSchemaWithJSON; - outputSchema?: StandardSchemaWithJSON; + inputSchema?: ZodRawShape | StandardSchemaWithJSON; + outputSchema?: ZodRawShape | StandardSchemaWithJSON; annotations?: ToolAnnotations; _meta?: Record; } @@ -217,6 +255,31 @@ export function registerAppTool< outputSchema?: OutputArgs; }, cb: ToolCallback, +): RegisteredTool; +/** + * @deprecated Wrap with `z.object({...})` instead. Raw-shape form: + * `inputSchema`/`outputSchema` may be a plain `{ field: z.string() }` record; + * the SDK auto-wraps it with `z.object()`. + */ +export function registerAppTool< + InputArgs extends ZodRawShape, + OutputArgs extends ZodRawShape | StandardSchemaWithJSON | undefined = + undefined, +>( + server: Pick, + name: string, + config: McpUiAppToolConfig & { + inputSchema?: InputArgs; + outputSchema?: OutputArgs; + }, + cb: LegacyToolCallback, +): RegisteredTool; +export function registerAppTool( + server: Pick, + name: string, + config: McpUiAppToolConfig, + // Widest callback shape accepted by either overload above. + cb: (...args: never[]) => unknown, ): RegisteredTool { // Normalize metadata for backward compatibility: // - If _meta.ui.resourceUri is set, also set the legacy flat key @@ -234,7 +297,16 @@ export function registerAppTool< normalizedMeta = { ...meta, ui: { ...uiMeta, resourceUri: legacyUri } }; } - return server.registerTool(name, { ...config, _meta: normalizedMeta }, cb); + // The public overloads above guarantee `config`/`cb` match one of the SDK's + // own registerTool overloads; the union-typed implementation cannot select + // between them, so widen for the forwarding call. + return server.registerTool( + name, + { ...config, _meta: normalizedMeta } as Parameters< + McpServer["registerTool"] + >[1], + cb as ToolCallback, + ); } export type McpUiReadResourceResult = ReadResourceResult & { From 7f686ff2b47b518db2f106db1710ead2ceea3227 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:28:23 +0100 Subject: [PATCH 31/45] Update dependency isolation check for the required client peer The app-only consumer now also exercises the ./react entry. The server-only consumer no longer asserts that client is absent from node_modules (npm auto-installs required peers) but still checks that the server entry does not bundle it. Surface npm and esbuild errors instead of swallowing them. --- scripts/check-dependency-isolation.mjs | 62 ++++++++++++++++++-------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/scripts/check-dependency-isolation.mjs b/scripts/check-dependency-isolation.mjs index 2dfe577..3809224 100644 --- a/scripts/check-dependency-isolation.mjs +++ b/scripts/check-dependency-isolation.mjs @@ -17,13 +17,20 @@ const packageJson = JSON.parse( const client = "@modelcontextprotocol/client"; const server = "@modelcontextprotocol/server"; +// App and AppBridge extend the client package's Protocol class, so client is +// a required peer for every consumer. The server helpers are only needed by +// server authors, so server stays optional: View-only consumers must not have +// it installed or bundled. for (const role of [client, server]) { if (!packageJson.peerDependencies?.[role]) { throw new Error(`${role} must remain a peer dependency`); } - if (packageJson.peerDependenciesMeta?.[role]?.optional !== true) { - throw new Error(`${role} must be an optional peer dependency`); - } +} +if (packageJson.peerDependenciesMeta?.[client]?.optional) { + throw new Error(`${client} must be a required peer dependency`); +} +if (packageJson.peerDependenciesMeta?.[server]?.optional !== true) { + throw new Error(`${server} must be an optional peer dependency`); } /** @@ -42,6 +49,13 @@ function exactDevDependency(name) { return version; } +/** Exact version of a package as installed in this repository's node_modules. */ +function installedVersion(name) { + return JSON.parse( + readFileSync(join(root, "node_modules", name, "package.json"), "utf8"), + ).version; +} + const temporaryRoot = mkdtempSync(join(tmpdir(), "ext-apps-role-peers-")); try { // Minimal environment: nothing inherited from the caller's npm config @@ -72,17 +86,25 @@ try { const consumers = [ { + // View / host author: ext-apps + client (+ react for the hooks entry). name: "app-only", dependencies: { "@types/node": exactDevDependency("@types/node"), + "@types/react": installedVersion("@types/react"), [client]: exactDevDependency(client), "@modelcontextprotocol/ext-apps": `file:${tarball}`, + react: installedVersion("react"), }, absent: server, + // server must be neither installed (it is an optional peer) nor bundled. + mustNotInstall: true, entry: - 'import { App } from "@modelcontextprotocol/ext-apps"; import { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; console.log(App, AppBridge);', + 'import { App } from "@modelcontextprotocol/ext-apps"; import { AppBridge } from "@modelcontextprotocol/ext-apps/app-bridge"; import { useApp } from "@modelcontextprotocol/ext-apps/react"; console.log(App, AppBridge, useApp);', }, { + // Server author: ext-apps + server. npm auto-installs client as a + // required peer (its types back the shared wire types), but the server + // entry must not pull it into a runtime bundle. name: "server-only", dependencies: { "@types/node": exactDevDependency("@types/node"), @@ -90,6 +112,7 @@ try { [server]: exactDevDependency(server), }, absent: client, + mustNotInstall: false, entry: 'import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; console.log(registerAppTool);', }, @@ -115,22 +138,24 @@ try { "--no-audit", "--no-fund", ], - { cwd: directory, stdio: "pipe", env: npmEnvironment }, + { cwd: directory, stdio: "inherit", env: npmEnvironment }, ); - const absentPath = join( - directory, - "node_modules", - ...consumer.absent.split("/"), - "package.json", - ); - try { - readFileSync(absentPath); - throw new Error( - `${consumer.name} unexpectedly installed ${consumer.absent}`, + if (consumer.mustNotInstall) { + const absentPath = join( + directory, + "node_modules", + ...consumer.absent.split("/"), + "package.json", ); - } catch (error) { - if (error?.code !== "ENOENT") throw error; + try { + readFileSync(absentPath); + throw new Error( + `${consumer.name} unexpectedly installed ${consumer.absent}`, + ); + } catch (error) { + if (error?.code !== "ENOENT") throw error; + } } writeFileSync(join(directory, "entry.ts"), consumer.entry); @@ -138,6 +163,7 @@ try { join(directory, "tsconfig.json"), JSON.stringify({ compilerOptions: { + jsx: "react-jsx", lib: ["ES2020", "DOM"], module: "ESNext", moduleResolution: "bundler", @@ -165,7 +191,7 @@ try { "--outfile=bundle.js", `--metafile=${metafile}`, ], - { cwd: directory, stdio: "pipe" }, + { cwd: directory, stdio: "inherit" }, ); const bundleInputs = Object.keys( JSON.parse(readFileSync(metafile, "utf8")).inputs, From 0a477bb25da7c5b0fdb88ab4f917c071f26c3729 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:28:53 +0100 Subject: [PATCH 32/45] Use explicit .js extensions in relative imports Consumers using moduleResolution NodeNext need explicit extensions in the emitted declaration files. Covers every source file except app.ts, app-bridge.ts and events.ts (and their tests), which are being reworked separately. --- src/events.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events.test.ts b/src/events.test.ts index 7fd8708..60e7c35 100644 --- a/src/events.test.ts +++ b/src/events.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, spyOn } from "bun:test"; -import { EventDispatcher } from "./events"; +import { EventDispatcher } from "./events.js"; type TestEventMap = { change: { value: number }; From 09bf2aae3cc3d7911894300efe95e6099c43238f Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:29:42 +0100 Subject: [PATCH 33/45] Drop the zod 3 JSON Schema fallback The zod peer range is ^4.2.0, whose schemas implement ~standard.jsonSchema, so the lazy zod/v4 toJSONSchema fallback for zod 3.25 is dead code. Re-export StandardSchemaWithJSON from the client package instead of keeping a structurally identical local copy. --- src/standard-schema.ts | 44 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/src/standard-schema.ts b/src/standard-schema.ts index 4bb85de..a06864a 100644 --- a/src/standard-schema.ts +++ b/src/standard-schema.ts @@ -4,36 +4,22 @@ import type { StandardTypedV1, } from "@standard-schema/spec"; -export type { StandardJSONSchemaV1, StandardSchemaV1, StandardTypedV1 }; - -// TODO(sdk-v2): once @modelcontextprotocol/core v2 is stable, import -// StandardSchemaWithJSON / standardSchemaToJsonSchema / validateStandardSchema -// from there and delete this file. At that point decide whether to tighten -// App.registerTool to StandardSchemaWithJSON (drops zod 3 from the peer range -// and the lazy z.toJSONSchema fallback below). +import type { StandardSchemaWithJSON } from "@modelcontextprotocol/client"; /** * A schema that implements both Standard Schema (validation) and Standard JSON * Schema (serialization). Zod v4, ArkType, and Valibot (via - * `@valibot/to-json-schema`) all satisfy this. - * - * Mirrors the type of the same name in `@modelcontextprotocol/core` v2 so that - * bumping to that package later is a drop-in import swap. + * `@valibot/to-json-schema`) all satisfy this. Re-exported from the SDK so + * View authors can import it alongside {@link app!App `App`}. * * @see https://standardschema.dev/ - * @see https://github.com/modelcontextprotocol/typescript-sdk/pull/1689 */ -export interface StandardSchemaWithJSON { - readonly "~standard": StandardSchemaV1.Props & - StandardJSONSchemaV1.Props; -} - -export namespace StandardSchemaWithJSON { - export type InferInput = - StandardTypedV1.InferInput; - export type InferOutput = - StandardTypedV1.InferOutput; -} +export type { + StandardJSONSchemaV1, + StandardSchemaV1, + StandardSchemaWithJSON, + StandardTypedV1, +}; /** JSON-Schema target draft used for tool input/output schemas (matches core MCP). */ const TARGET = { target: "draft-2020-12" } as const; @@ -41,11 +27,8 @@ const TARGET = { target: "draft-2020-12" } as const; /** * Serialize a Standard Schema to JSON Schema for the given direction. * - * Uses `~standard.jsonSchema` when present (zod v4, ArkType, Valibot, …). - * Falls back to a lazy `zod/v4` import for zod v3.25.x — which implements - * `~standard.validate` but not yet `~standard.jsonSchema` — so the existing - * `^3.25.0 || ^4.0.0` peer range keeps working. Non-zod schemas without - * `jsonSchema` throw. + * Requires `~standard.jsonSchema` (zod v4, ArkType, Valibot, …); schemas + * without it throw. */ export async function standardSchemaToJsonSchema( schema: StandardSchemaV1, @@ -57,11 +40,6 @@ export async function standardSchemaToJsonSchema( if (std.jsonSchema) { return std.jsonSchema[io](TARGET); } - if (std.vendor === "zod") { - const { z } = await import("zod/v4"); - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- bridging StandardSchemaV1 → zod's $ZodType for the v3.25 fallback - return z.toJSONSchema(schema as any, { io }); - } throw new Error( `Schema (vendor: ${std.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). ` + `Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`, From c5b25431155d72a0a2cb4f45f635e243ad1ea844 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:31:22 +0100 Subject: [PATCH 34/45] Document the 2.x install matrix and add a migration guide Install instructions now list the split base MCP SDK packages at ^2.0.0 by role (core comes in transitively, server only for server authors), require Node 20+, and state that the wire protocol is unchanged so 2.x Views and 1.x hosts interoperate. The agent skills lose the exact prerelease wording and add-app-to-server gains a v1 to v2 import mapping. docs/migrate-to-2.md lists the breaking changes, the peer matrix and the host-side error deltas. --- AGENTS.md | 4 +- README.md | 22 +++-- docs/migrate-to-2.md | 94 +++++++++++++++++++ docs/overview.md | 4 +- docs/quickstart.md | 2 +- docs/testing-mcp-apps.md | 2 +- examples/transcript-server/README.md | 2 +- .../skills/add-app-to-server/SKILL.md | 22 ++++- .../mcp-apps/skills/convert-web-app/SKILL.md | 8 +- .../mcp-apps/skills/create-mcp-app/SKILL.md | 8 +- typedoc.config.mjs | 2 + 11 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 docs/migrate-to-2.md diff --git a/AGENTS.md b/AGENTS.md index 4b0faa0..219b947 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,8 +60,8 @@ rm -fR package-lock.json node_modules && \ ### Key Source Files -- `src/app.ts` - `App` subclasses the base MCP SDK `Protocol`, handles View initialization, tool calls, and messaging -- `src/app-bridge.ts` - `AppBridge` subclasses the base MCP SDK `Protocol` for the iframe channel and proxies through a separate outer `Client` +- `src/app.ts` - `App` subclasses `Protocol` from `@modelcontextprotocol/client`, handles View initialization, tool calls, and messaging +- `src/app-bridge.ts` - `AppBridge` subclasses `Protocol` from `@modelcontextprotocol/client` for the iframe channel and proxies through a separate outer `Client` - `src/server/index.ts` - Helpers for MCP servers to register tools/resources with UI metadata - `src/types.ts` - Protocol types re-exported from `spec.types.ts` and Zod schemas from `generated/schema.ts` (auto-generated during build) - `src/message-transport.ts` - `PostMessageTransport` for iframe communication diff --git a/README.md b/README.md index ff47e1e..4b92f0c 100644 --- a/README.md +++ b/README.md @@ -122,27 +122,33 @@ resources: ## Getting Started +Requires Node.js 20+. The base MCP SDK packages are `^2.0.0` peers of +`ext-apps` (`@modelcontextprotocol/core` is pulled in transitively). + For a View or host: ```bash npm install -S @modelcontextprotocol/ext-apps \ - @modelcontextprotocol/client@2.0.0-beta.5 \ - @modelcontextprotocol/core@2.0.0-beta.5 \ + @modelcontextprotocol/client@^2.0.0 \ zod@^4.2.0 ``` -For an MCP server: +For an MCP server, add the server package (and, for HTTP transports, the Node +and Express adapters): ```bash npm install -S @modelcontextprotocol/ext-apps \ - @modelcontextprotocol/server@2.0.0-beta.5 \ - @modelcontextprotocol/core@2.0.0-beta.5 \ + @modelcontextprotocol/client@^2.0.0 \ + @modelcontextprotocol/server@^2.0.0 \ + @modelcontextprotocol/node@^2.0.0 \ + @modelcontextprotocol/express@^2.0.0 \ zod@^4.2.0 ``` -Applications that implement both roles should install both `client` and -`server`. Keep all installed base MCP SDK packages on the exact same published -beta so they share one compatible protocol implementation. +The wire protocol is unchanged between `ext-apps` 1.x and 2.x: a 2.x View works +in a 1.x host and a 2.x host renders 1.x Views. See the +[migration guide](https://apps.extensions.modelcontextprotocol.io/api/documents/migrate-to-v2.html) +when upgrading from 1.x. **New here?** Start with the [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html) diff --git a/docs/migrate-to-2.md b/docs/migrate-to-2.md new file mode 100644 index 0000000..3f6db7b --- /dev/null +++ b/docs/migrate-to-2.md @@ -0,0 +1,94 @@ +--- +title: Migrate to v2 +group: Getting Started +description: Upgrade from ext-apps 1.x to 2.x — new base MCP SDK peer packages, API changes for Views and hosts, and what stays wire-compatible. +--- + +# Migrating from ext-apps 1.x to 2.x + +ext-apps 2.x is built on the base MCP TypeScript SDK 2.x, which replaced the +single `@modelcontextprotocol/sdk` package with `@modelcontextprotocol/client`, +`server`, `core`, `node` and `express`. The MCP Apps wire protocol did not +change; the breaking changes are in dependencies and in the TypeScript API. + +## Host compatibility + +The `ui/*` messages exchanged over the iframe channel are byte-identical to +1.x. A 2.x View runs in a 1.x host and a 2.x host renders 1.x Views. The only +host-side deltas are in error responses (see below). + +## Peer dependencies by role + +| Role | Install | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| View author | `@modelcontextprotocol/ext-apps`, `@modelcontextprotocol/client@^2.0.0`, `zod@^4.2.0` (+ `react`/`react-dom` for `./react`) | +| Host author | same as View author | +| MCP server author | View author packages + `@modelcontextprotocol/server@^2.0.0`; `@modelcontextprotocol/node` and `express` for HTTP transports | +| CDN / `*-with-deps` | nothing extra: `./app-with-deps` and `./react-with-deps` bundle client, core and zod (about 25% larger than the 1.x bundles) | + +`@modelcontextprotocol/client` is a required peer (`App` and `AppBridge` extend +its `Protocol` class); `@modelcontextprotocol/core` is installed transitively; +`@modelcontextprotocol/server` stays optional and is only needed for the +`./server` helpers. Node.js 20+ is required. + +## Breaking changes + +- **Peer packages.** `@modelcontextprotocol/sdk@^1` is replaced by the split + 2.x packages above, all at `^2.0.0`. Remove the 1.x package from your + project; the two SDKs do not interoperate. +- **zod 3 dropped.** The peer range is `zod@^4.2.0`. Tool schemas must + implement Standard JSON Schema (`~standard.jsonSchema`): zod v4, ArkType, + Valibot. Raw zod shapes (`{ q: z.string() }`) still work with + `registerAppTool` but are deprecated; wrap them with `z.object({...})`. +- **`App` / `AppBridge` extend `Protocol` from `@modelcontextprotocol/client`.** + `ProtocolWithEvents`, `AppRequest`, `AppNotification` and `AppResult` are + gone; use the SDK's `Protocol`, `Request`, `Notification` and `Result`. +- **Handler context.** Custom handlers receive the SDK 2.x context: + `extra.signal` → `extra.mcpReq.signal`, `extra.requestId` → + `extra.mcpReq.id`. +- **`setRequestHandler` / `setNotificationHandler` take method names.** + `app.setRequestHandler(SomeRequestSchema, handler)` becomes + `app.setRequestHandler("some/method", handler)` (pass the params schema as the + optional extra argument for validation). +- **Errors.** Remote JSON-RPC errors are `ProtocolError` (numeric `code`); + local conditions are `SdkError` with a string `code`: request timeout → + `"REQUEST_TIMEOUT"`, connection closed → `"CONNECTION_CLOSED"`. Cancelling a + request with an `AbortSignal` also rejects with `"REQUEST_TIMEOUT"` (the + message is the abort reason). Messages no longer carry the `MCP error N:` + prefix. + +## Host-side wire deltas + +Observed when a 2.x `AppBridge` answers a View; a 1.x View sees these too. + +| Situation | 1.x host | 2.x host | +| --------------------------------------------------------- | ------------------------------ | --------------------------------------------------- | +| A handler throws `-32002` (resource not found) | `error.code: -32002` | `error.code: -32602` (the SDK never emits `-32002`) | +| Invalid params on a `ui/*` request | `-32603` with a zod issue dump | `-32602 Invalid params for : …` | +| Error message text | `MCP error -32602: …` prefix | plain message | +| `tools/call` to an unknown tool through a 2.x `McpServer` | `result.isError: true` | JSON-RPC error `-32602` (`callServerTool` rejects) | + +## `schema.json` + +The published `./schema.json` export is regenerated from the 2.x core schemas: + +- `McpUiToolResultNotification.params.structuredContent` is any JSON value (was + `type: "object"`). +- Result `_meta` documents `io.modelcontextprotocol/serverInfo` and no longer + lists `progressToken` / `related-task` (both still pass through). +- `McpUiHostContext.toolInfo.tool.inputSchema` is a loose object. +- A recursive `$defs.__schema0` JSON-value definition is added. + +## Checklist + +1. `npm uninstall @modelcontextprotocol/sdk` and install the packages for your + role from the table above. +2. Replace `sdk/...` imports with the split packages (`sdk/server/mcp.js` → + `@modelcontextprotocol/server`, `sdk/server/streamableHttp.js` → + `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node`, + `sdk/server/stdio.js` → `@modelcontextprotocol/server/stdio`, `sdk/types.js` + → `@modelcontextprotocol/client` or `server`). +3. Wrap raw zod shapes with `z.object({...})`. +4. Update custom handlers to the `extra.mcpReq.*` context and method-keyed + `setRequestHandler` calls. +5. Replace `McpError` / numeric-code checks with `ProtocolError` / `SdkError`. diff --git a/docs/overview.md b/docs/overview.md index 5f3852d..8eda95d 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -56,8 +56,8 @@ flowchart LR - **Host** — The chat client (e.g., Claude Desktop) that connects to servers, embeds Views in iframes, and proxies communication between them. - **View** — The UI running inside a sandboxed iframe. It receives tool data from the Host and can call server tools or send messages back to the chat. -The View's `App` and the Host's `AppBridge` both subclass the base MCP SDK's -public `Protocol` for the iframe channel. A separate outer `Client` connects +The View's `App` and the Host's `AppBridge` both subclass the `Protocol` class +exported by `@modelcontextprotocol/client` for the iframe channel. A separate outer `Client` connects the Host to the actual MCP Server. Keeping those two connections separate preserves the Apps-only iframe handshake and prevents iframe capabilities from leaking into the server connection. diff --git a/docs/quickstart.md b/docs/quickstart.md index 3cac872..2039dc1 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -37,7 +37,7 @@ Install the dependencies you'll need: ```bash npm init -y -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 @modelcontextprotocol/node@2.0.0-beta.5 @modelcontextprotocol/express@2.0.0-beta.5 zod@^4.2.0 express cors +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@^2.0.0 @modelcontextprotocol/server@^2.0.0 @modelcontextprotocol/node@^2.0.0 @modelcontextprotocol/express@^2.0.0 zod@^4.2.0 express cors npm install -D typescript vite vite-plugin-singlefile @types/express @types/cors @types/node tsx concurrently cross-env ``` diff --git a/docs/testing-mcp-apps.md b/docs/testing-mcp-apps.md index e5d82b5..a8eb333 100644 --- a/docs/testing-mcp-apps.md +++ b/docs/testing-mcp-apps.md @@ -29,7 +29,7 @@ The [`basic-host`](https://github.com/modelcontextprotocol/ext-apps/tree/main/ex ``` The root install runs the package build, including type-checking the - documentation snippets against the exact base MCP SDK beta.4 packages. Do + documentation snippets against the pinned base MCP SDK 2.x packages. Do not bypass a failed build before testing the host. 2. Start basic-host, pointing it to your MCP server: diff --git a/examples/transcript-server/README.md b/examples/transcript-server/README.md index 3b3fa42..559ffad 100644 --- a/examples/transcript-server/README.md +++ b/examples/transcript-server/README.md @@ -56,7 +56,7 @@ To test local modifications, use this configuration (replace `~/code/ext-apps` w ### Prerequisites -- Node.js 18+ +- Node.js 20+ - Chrome, Edge, or Safari (Web Speech API support) ### Installation diff --git a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md index fda72b0..df7d931 100644 --- a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md +++ b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md @@ -77,15 +77,27 @@ Before writing any code, analyze the server's existing tools and determine which ## Step 2: Add Dependencies ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 zod@^4.2.0 +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@^2.0.0 @modelcontextprotocol/server@^2.0.0 zod@^4.2.0 npm install -D vite vite-plugin-singlefile ``` -Plus framework-specific dependencies if needed (e.g., `react`, `react-dom`, `@vitejs/plugin-react` for React). +Plus framework-specific dependencies if needed (e.g., `react`, `react-dom`, `@vitejs/plugin-react` for React), and `@modelcontextprotocol/node` / `@modelcontextprotocol/express` if the server uses HTTP transports. -Use the exact base MCP SDK prerelease required by ext-apps. Existing servers -that still import `@modelcontextprotocol/sdk` v1 must migrate those imports and -handler schemas before adding the App integration. +ext-apps 2.x requires the split base MCP SDK packages at `^2.0.0` +(`@modelcontextprotocol/core` comes in transitively). Existing servers that +still import `@modelcontextprotocol/sdk` v1 must migrate those imports and +handler schemas before adding the App integration: + +| SDK v1 (`@modelcontextprotocol/sdk`) | SDK v2 | +|---|---| +| `sdk/server/mcp.js` (`McpServer`) | `@modelcontextprotocol/server` | +| `sdk/server/streamableHttp.js` (`StreamableHTTPServerTransport`) | `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node` | +| Express wiring by hand | `createMcpExpressApp` from `@modelcontextprotocol/express` | +| `sdk/server/stdio.js` | `@modelcontextprotocol/server/stdio` | +| `sdk/types.js` (types, schemas) | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | +| Raw zod shapes: `inputSchema: { q: z.string() }` | `inputSchema: z.object({ q: z.string() })` | +| `extra.signal` in tool callbacks | `extra.mcpReq.signal` | +| `setRequestHandler(SomeRequestSchema, handler)` | `setRequestHandler("some/method", handler)` | ## Step 3: Set Up the Build Pipeline diff --git a/plugins/mcp-apps/skills/convert-web-app/SKILL.md b/plugins/mcp-apps/skills/convert-web-app/SKILL.md index 03fc8d7..8fb7fa0 100644 --- a/plugins/mcp-apps/skills/convert-web-app/SKILL.md +++ b/plugins/mcp-apps/skills/convert-web-app/SKILL.md @@ -115,12 +115,14 @@ Create a new MCP server with tool and resource registration. This wraps the exis ### Dependencies ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 zod@^4.2.0 +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@^2.0.0 @modelcontextprotocol/server@^2.0.0 zod@^4.2.0 npm install -D tsx vite vite-plugin-singlefile ``` -Use `npm install` with the exact base MCP SDK prerelease required by ext-apps. -Do not substitute unpublished local packages or guess a different prerelease. +Use `npm install` so the package manager resolves versions; ext-apps 2.x needs +the split base MCP SDK packages at `^2.0.0` (`@modelcontextprotocol/core` comes +in transitively). Do not add the legacy `@modelcontextprotocol/sdk` v1 package +or substitute unpublished local packages. ### Server Code diff --git a/plugins/mcp-apps/skills/create-mcp-app/SKILL.md b/plugins/mcp-apps/skills/create-mcp-app/SKILL.md index 6f1c466..d73dc90 100644 --- a/plugins/mcp-apps/skills/create-mcp-app/SKILL.md +++ b/plugins/mcp-apps/skills/create-mcp-app/SKILL.md @@ -110,12 +110,14 @@ See `/tmp/mcp-ext-apps/docs/patterns.md` for detailed recipes: **Always** use `npm install` to add dependencies rather than manually writing version numbers: ```bash -npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.5 @modelcontextprotocol/core@2.0.0-beta.5 @modelcontextprotocol/server@2.0.0-beta.5 @modelcontextprotocol/node@2.0.0-beta.5 @modelcontextprotocol/express@2.0.0-beta.5 zod@^4.2.0 express cors +npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@^2.0.0 @modelcontextprotocol/server@^2.0.0 @modelcontextprotocol/node@^2.0.0 @modelcontextprotocol/express@^2.0.0 zod@^4.2.0 express cors npm install -D typescript vite vite-plugin-singlefile concurrently cross-env @types/node @types/express @types/cors ``` -Use the exact base MCP SDK prerelease required by ext-apps. Do not substitute -unpublished local packages or guess a different prerelease. +ext-apps 2.x requires the split base MCP SDK packages (`@modelcontextprotocol/client`, +`server`, `node`, `express`) at `^2.0.0`; `@modelcontextprotocol/core` comes in +transitively. Do not add the legacy `@modelcontextprotocol/sdk` v1 package or +substitute unpublished local packages. ### TypeScript Server Execution diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 1531bdc..c9b9476 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -19,6 +19,7 @@ const config = { "docs/authorization.md", "docs/csp-cors.md", "docs/migrate_from_openai_apps.md", + "docs/migrate-to-2.md", ], entryPoints: [ "src/server/index.ts", @@ -43,6 +44,7 @@ const config = { ResponseCacheStore: BASE_SDK_DOCS, SdkError: BASE_SDK_DOCS, "SdkErrorCode.MethodNotSupportedByProtocolVersion": BASE_SDK_DOCS, + StandardJSONSchemaV1: BASE_SDK_DOCS, "SdkErrorCode.UnsupportedResultType": BASE_SDK_DOCS, "__type.enforceStrictCapabilities": BASE_SDK_DOCS, }, From ea6b89c9e2670b9ef73ad8b61d989cf6f2f1cc05 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:33:01 +0100 Subject: [PATCH 35/45] Add wire compatibility tests against captured 1.x traffic Replay JSON-RPC messages recorded from ext-apps 1.7.x (sdk 1.x key order and ids) through the 2.x App and AppBridge over a raw in-memory transport and assert exactly what 2.x emits: the ui/initialize handshake, params-less ui/notifications/initialized, tools/call in both directions, and the -32002 to -32602 re-encoding on the host side. Also check that AppBridge never sends an MCP initialize toward the View and that the outer-client proxy works with a default (non-legacy) 2.x Client/Server pair. --- src/wire-compat.test.ts | 576 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100644 src/wire-compat.test.ts diff --git a/src/wire-compat.test.ts b/src/wire-compat.test.ts new file mode 100644 index 0000000..5fdca8b --- /dev/null +++ b/src/wire-compat.test.ts @@ -0,0 +1,576 @@ +/** + * Cross-version wire compatibility. + * + * Replays JSON-RPC messages captured from ext-apps 1.7.x (built on + * `@modelcontextprotocol/sdk` 1.x) through the 2.x `App` and `AppBridge` over + * a raw in-memory transport, and asserts what 2.x emits back. The MCP Apps + * wire protocol is meant to be unchanged across the major bump; the few + * host-side error deltas are pinned here so they stay deliberate. + */ +import { describe, it, expect, afterEach } from "bun:test"; +import { + Client, + InMemoryTransport, + ProtocolError, + type JSONRPCMessage, + type Transport, +} from "@modelcontextprotocol/client"; +import { McpServer } from "@modelcontextprotocol/server"; +import { z } from "zod/v4"; + +import { App } from "./app.js"; +import { + AppBridge, + type McpUiHostCapabilities, + type McpUiHostContext, +} from "./app-bridge.js"; +import { LATEST_PROTOCOL_VERSION } from "./types.js"; + +/** Wait for pending microtasks/timers to complete. */ +const flush = () => new Promise((resolve) => setTimeout(resolve, 5)); + +/** + * One end of a raw in-memory channel. The 2.x side gets `transport`; the test + * plays the role of the 1.x peer by calling `inject()` with captured JSON and + * reading `sent` to see exactly what 2.x put on the wire. + */ +function createRawChannel() { + const sent: JSONRPCMessage[] = []; + let closed = false; + const transport: Transport = { + async start() {}, + async send(message) { + // Serialize like a real transport would so we assert on plain JSON. + sent.push(JSON.parse(JSON.stringify(message))); + }, + async close() { + if (closed) return; + closed = true; + transport.onclose?.(); + }, + }; + return { + transport, + sent, + /** Deliver a raw message (as the 1.x peer would have sent it). */ + inject(message: unknown) { + transport.onmessage?.(message as JSONRPCMessage); + }, + /** Messages sent after the given index. */ + since(index: number) { + return sent.slice(index); + }, + }; +} + +const hostInfo = { name: "TestHost", version: "9.9.9" }; +const hostCapabilities: McpUiHostCapabilities = { + openLinks: {}, + serverTools: {}, + serverResources: {}, + logging: {}, + updateModelContext: {}, +}; +const hostContext: McpUiHostContext = { + theme: "dark", + displayMode: "inline", + locale: "en-US", +}; + +/** Captured from ext-apps 1.7.x View (sdk 1.x key order, numeric ids from 0). */ +const v1View = { + initialize: { + method: "ui/initialize", + params: { + appCapabilities: { tools: { listChanged: true } }, + appInfo: { name: "TestView", version: "1.2.3" }, + protocolVersion: "2026-01-26", + }, + jsonrpc: "2.0", + id: 0, + }, + initialized: { method: "ui/notifications/initialized", jsonrpc: "2.0" }, + callTool: { + method: "tools/call", + params: { name: "echo", arguments: { a: 1 }, _meta: { progressToken: 6 } }, + jsonrpc: "2.0", + id: 6, + }, + readResource: { + method: "resources/read", + params: { uri: "test://x" }, + jsonrpc: "2.0", + id: 12, + }, + toolsListResult: (id: number) => ({ + result: { + tools: [ + { + name: "viewtool", + description: "view tool", + inputSchema: { + type: "object", + properties: { x: { type: "number" } }, + }, + }, + ], + }, + jsonrpc: "2.0", + id, + }), +}; + +/** Captured from ext-apps 1.7.x AppBridge (sdk 1.x). */ +const v1Host = { + initializeResult: (id: number) => ({ + result: { + protocolVersion: "2026-01-26", + hostCapabilities, + hostInfo, + hostContext, + }, + jsonrpc: "2.0", + id, + }), + toolInput: { + method: "ui/notifications/tool-input", + params: { arguments: { location: "NYC" } }, + jsonrpc: "2.0", + }, + callToolResult: (id: number) => ({ + result: { content: [{ type: "text", text: "echo 1" }] }, + jsonrpc: "2.0", + id, + }), + /** McpServer 1.x reports an unknown tool as an isError result, not an error. */ + callToolUnknownResult: (id: number) => ({ + result: { + content: [ + { type: "text", text: "MCP error -32602: Tool nope not found" }, + ], + isError: true, + }, + jsonrpc: "2.0", + id, + }), + resourceNotFoundError: (id: number) => ({ + jsonrpc: "2.0", + id, + error: { + code: -32002, + message: "MCP error -32002: Resource not found (host-thrown -32002)", + }, + }), + toolsList: { method: "tools/list", jsonrpc: "2.0", id: 0 }, + callViewTool: { + method: "tools/call", + params: { name: "viewtool", arguments: { x: 7 } }, + jsonrpc: "2.0", + id: 1, + }, +}; + +describe("wire compatibility: 2.x AppBridge with a 1.x View", () => { + let bridge: AppBridge; + + afterEach(async () => { + await bridge?.close().catch(() => {}); + }); + + async function connectBridge() { + const channel = createRawChannel(); + bridge = new AppBridge(null, hostInfo, hostCapabilities, { hostContext }); + await bridge.connect(channel.transport); + return channel; + } + + async function handshake() { + const channel = await connectBridge(); + let initialized = 0; + bridge.oninitialized = () => { + initialized++; + }; + channel.inject(v1View.initialize); + await flush(); + channel.inject(v1View.initialized); + await flush(); + return { channel, initialized: () => initialized }; + } + + it("sends nothing on connect: no MCP initialize toward the View", async () => { + const channel = await connectBridge(); + await flush(); + expect(channel.sent).toEqual([]); + }); + + it("answers a 1.x ui/initialize with the 1.x result shape and same id", async () => { + const channel = await connectBridge(); + channel.inject(v1View.initialize); + await flush(); + + expect(channel.sent).toEqual([ + { + jsonrpc: "2.0", + id: 0, + result: { + protocolVersion: LATEST_PROTOCOL_VERSION, + hostCapabilities, + hostInfo, + hostContext, + }, + }, + ]); + expect(bridge.getAppCapabilities()).toEqual({ + tools: { listChanged: true }, + }); + expect(bridge.getAppVersion()).toEqual({ + name: "TestView", + version: "1.2.3", + }); + }); + + it("accepts ui/notifications/initialized without params", async () => { + const { channel, initialized } = await handshake(); + expect(initialized()).toBe(1); + // Only the initialize response went out; the notification gets no reply + // and the bridge never starts an MCP handshake of its own. + expect(channel.sent).toHaveLength(1); + expect(channel.sent.some((m) => "method" in m)).toBe(false); + }); + + it("answers a 1.x tools/call request through oncalltool", async () => { + const { channel } = await handshake(); + bridge.oncalltool = async (params) => ({ + content: [{ type: "text", text: `echo ${params.arguments?.a}` }], + }); + const mark = channel.sent.length; + channel.inject(v1View.callTool); + await flush(); + + expect(channel.since(mark)).toEqual([ + { + jsonrpc: "2.0", + id: 6, + result: { content: [{ type: "text", text: "echo 1" }] }, + }, + ]); + }); + + it("re-encodes a handler-thrown -32002 as -32602 without the MCP error prefix", async () => { + // Documented host-side delta: SDK 2.x never emits -32002 on the wire. + const { channel } = await handshake(); + bridge.onreadresource = async () => { + throw new ProtocolError( + -32002, + "Resource not found (host-thrown -32002)", + ); + }; + const mark = channel.sent.length; + channel.inject(v1View.readResource); + await flush(); + + const [reply] = channel.since(mark); + expect(reply).toMatchObject({ + jsonrpc: "2.0", + id: 12, + error: { code: -32602 }, + }); + const message = (reply as { error: { message: string } }).error.message; + expect(message).toBe("Resource not found (host-thrown -32002)"); + expect(message).not.toMatch(/^MCP error/); + }); + + it("rejects invalid params on a ui/* request with -32602", async () => { + const { channel } = await handshake(); + bridge.onopenlink = async () => ({}); + const mark = channel.sent.length; + channel.inject({ + method: "ui/open-link", + params: { url: 42 }, + jsonrpc: "2.0", + id: 14, + }); + await flush(); + + expect(channel.since(mark)).toHaveLength(1); + expect(channel.since(mark)[0]).toMatchObject({ + jsonrpc: "2.0", + id: 14, + error: { code: -32602 }, + }); + }); + + it("lists and calls View tools with 1.x-shaped responses", async () => { + const { channel } = await handshake(); + const mark = channel.sent.length; + const listPromise = bridge.listTools({}); + await flush(); + + const [listRequest] = channel.since(mark) as Array<{ + id: number; + method: string; + }>; + expect(listRequest).toMatchObject({ jsonrpc: "2.0", method: "tools/list" }); + channel.inject(v1View.toolsListResult(listRequest.id)); + const list = await listPromise; + expect(list.tools.map((t) => t.name)).toEqual(["viewtool"]); + + const callPromise = bridge.callTool({ + name: "viewtool", + arguments: { x: 7 }, + }); + await flush(); + const [callRequest] = channel.since(mark + 1) as Array<{ id: number }>; + expect(callRequest).toMatchObject({ + jsonrpc: "2.0", + method: "tools/call", + params: { name: "viewtool", arguments: { x: 7 } }, + }); + channel.inject({ + result: { content: [{ type: "text", text: "view got 7" }] }, + jsonrpc: "2.0", + id: callRequest.id, + }); + expect(await callPromise).toEqual({ + content: [{ type: "text", text: "view got 7" }], + }); + }); + + it("only ever emits jsonrpc 2.0 messages, never an initialize request", async () => { + const { channel } = await handshake(); + bridge.oncalltool = async () => ({ content: [] }); + channel.inject(v1View.callTool); + bridge.sendToolInput({ arguments: { q: "x" } }); + bridge.sendToolResult({ content: [] }); + await flush(); + + for (const message of channel.sent) { + expect(message.jsonrpc).toBe("2.0"); + } + const methods = channel.sent + .filter((m): m is JSONRPCMessage & { method: string } => "method" in m) + .map((m) => m.method); + expect(methods).toEqual([ + "ui/notifications/tool-input", + "ui/notifications/tool-result", + ]); + expect(methods).not.toContain("initialize"); + expect(methods).not.toContain("notifications/initialized"); + }); +}); + +describe("wire compatibility: 2.x App with a 1.x host", () => { + let app: App; + + afterEach(async () => { + await app?.close().catch(() => {}); + }); + + /** Connect the App and answer its ui/initialize the way a 1.x host did. */ + async function handshake( + capabilities: ConstructorParameters[1] = {}, + setup: (app: App) => void = () => {}, + ) { + const channel = createRawChannel(); + app = new App({ name: "TestView", version: "1.2.3" }, capabilities, { + autoResize: false, + }); + setup(app); + const connected = app.connect(channel.transport); + await flush(); + + const [initialize] = channel.sent as Array<{ id: number }>; + channel.inject(v1Host.initializeResult(initialize.id)); + await connected; + await flush(); + return { channel, initialize }; + } + + it("sends a 1.x-compatible ui/initialize and ui/notifications/initialized", async () => { + const { channel } = await handshake({ tools: { listChanged: true } }); + + expect(channel.sent).toEqual([ + { + jsonrpc: "2.0", + id: 0, + method: "ui/initialize", + params: { + protocolVersion: LATEST_PROTOCOL_VERSION, + appInfo: { name: "TestView", version: "1.2.3" }, + appCapabilities: { tools: { listChanged: true } }, + }, + }, + { jsonrpc: "2.0", method: "ui/notifications/initialized" }, + ]); + expect(app.getHostContext()).toEqual(hostContext); + expect(app.getHostCapabilities()).toEqual(hostCapabilities); + expect(app.getHostVersion()).toEqual(hostInfo); + }); + + it("delivers a 1.x notification with sdk-1 key order to ontoolinput", async () => { + const received: unknown[] = []; + const { channel } = await handshake({}, (app) => { + app.ontoolinput = (params) => { + received.push(params); + }; + }); + channel.inject(v1Host.toolInput); + await flush(); + expect(received).toEqual([{ arguments: { location: "NYC" } }]); + }); + + it("resolves callServerTool from a 1.x tools/call response", async () => { + const { channel } = await handshake(); + const mark = channel.sent.length; + const promise = app.callServerTool({ name: "echo", arguments: { a: 1 } }); + await flush(); + + const [request] = channel.since(mark) as Array<{ id: number }>; + expect(request).toMatchObject({ + jsonrpc: "2.0", + method: "tools/call", + params: { + name: "echo", + arguments: { a: 1 }, + _meta: { progressToken: request.id }, + }, + }); + channel.inject(v1Host.callToolResult(request.id)); + expect(await promise).toEqual({ + content: [{ type: "text", text: "echo 1" }], + }); + }); + + it("passes through a 1.x isError tool result unchanged", async () => { + const { channel } = await handshake(); + const mark = channel.sent.length; + const promise = app.callServerTool({ name: "nope", arguments: {} }); + await flush(); + const [request] = channel.since(mark) as Array<{ id: number }>; + channel.inject(v1Host.callToolUnknownResult(request.id)); + expect(await promise).toEqual({ + content: [ + { type: "text", text: "MCP error -32602: Tool nope not found" }, + ], + isError: true, + }); + }); + + it("surfaces a 1.x -32002 error as a ProtocolError with code -32002", async () => { + const { channel } = await handshake(); + const mark = channel.sent.length; + const promise = app.readServerResource({ uri: "test://x" }); + await flush(); + const [request] = channel.since(mark) as Array<{ id: number }>; + channel.inject(v1Host.resourceNotFoundError(request.id)); + + const error = await promise.then( + () => undefined, + (e: unknown) => e, + ); + expect(error).toBeInstanceOf(ProtocolError); + expect((error as ProtocolError).code).toBe(-32002); + expect((error as ProtocolError).message).toContain( + "Resource not found (host-thrown -32002)", + ); + }); + + it("answers 1.x tools/list (no params) and tools/call requests from the host", async () => { + const { channel } = await handshake({ tools: { listChanged: true } }); + app.registerTool( + "viewtool", + { description: "view tool", inputSchema: z.object({ x: z.number() }) }, + async ({ x }) => ({ + content: [{ type: "text", text: `view got ${x}` }], + }), + ); + await flush(); + const mark = channel.sent.length; + + channel.inject(v1Host.toolsList); + await flush(); + const [listReply] = channel.since(mark) as unknown as Array<{ + result: { tools: Array<{ name: string; inputSchema: unknown }> }; + }>; + expect(listReply).toMatchObject({ jsonrpc: "2.0", id: 0 }); + expect(listReply.result.tools).toHaveLength(1); + expect(listReply.result.tools[0]).toMatchObject({ + name: "viewtool", + description: "view tool", + inputSchema: { + type: "object", + properties: { x: { type: "number" } }, + required: ["x"], + }, + }); + + channel.inject(v1Host.callViewTool); + await flush(); + expect(channel.since(mark + 1)).toEqual([ + { + jsonrpc: "2.0", + id: 1, + result: { content: [{ type: "text", text: "view got 7" }] }, + }, + ]); + }); +}); + +describe("AppBridge proxy over a default (modern era) 2.x Client/Server", () => { + let client: Client; + let server: McpServer; + let app: App; + let bridge: AppBridge; + + afterEach(async () => { + await app?.close().catch(() => {}); + await bridge?.close().catch(() => {}); + await client?.close().catch(() => {}); + await server?.close().catch(() => {}); + }); + + it("forwards tools/call and tools/list_changed with default negotiation", async () => { + server = new McpServer({ name: "ActualServer", version: "1.0.0" }); + server.registerTool( + "echo", + { inputSchema: z.object({ a: z.number() }) }, + async ({ a }) => ({ content: [{ type: "text", text: `echo ${a}` }] }), + ); + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + await server.connect(serverTransport); + + client = new Client({ name: "HostOuterClient", version: "1.0.0" }); + await client.connect(clientTransport); + expect(client.getServerCapabilities()?.tools).toEqual({ + listChanged: true, + }); + + bridge = new AppBridge(client, hostInfo, hostCapabilities, { + hostContext, + }); + app = new App( + { name: "TestView", version: "1.2.3" }, + {}, + { + autoResize: false, + }, + ); + const listChanged: string[] = []; + app.setNotificationHandler("notifications/tools/list_changed", () => { + listChanged.push("tools"); + }); + const [appTransport, bridgeTransport] = + InMemoryTransport.createLinkedPair(); + await bridge.connect(bridgeTransport); + await app.connect(appTransport); + + expect( + await app.callServerTool({ name: "echo", arguments: { a: 1 } }), + ).toMatchObject({ content: [{ type: "text", text: "echo 1" }] }); + + server.sendToolListChanged(); + await flush(); + expect(listChanged).toEqual(["tools"]); + }); +}); From cfdc0c725f03b41ba9f3ccb2299386a2369cb9ed Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:21:49 +0100 Subject: [PATCH 36/45] fix: restore double-registration guard on App and AppBridge handlers The base SDK Protocol silently replaces an existing request or notification handler. v1's ProtocolWithEvents threw instead, so a stray setRequestHandler / setNotificationHandler could not disconnect on* handlers or addEventListener listeners. Restore that guard on both App and AppBridge with a small MethodRegistry, keep replace semantics for the on* setters via a protected replaceRequestHandler, and release methods on removeRequestHandler / removeNotificationHandler. Also: - give oncreatesamplingmessage a getter, a replace warning and the same clearable shape as the other request setters - let App register handlers for custom (non-spec) methods; the default branch of assertRequestHandlerCapability threw a misleading "No handler for method X registered" error - move the RESOURCE_URI_META_KEY / RESOURCE_MIME_TYPE docs to src/constants.ts where the constants now live - fix stale "inherited from Server" wording and point @see links at @modelcontextprotocol/client, which is what hosts install --- src/app-bridge.test.ts | 85 +++++++++++++++++++- src/app-bridge.ts | 176 ++++++++++++++++++++++++++++++++--------- src/app.test.ts | 111 +++++++++++++++++++++++--- src/app.ts | 159 ++++++++++++++++++++----------------- src/constants.ts | 65 ++++++++++++++- src/events.ts | 41 ++++++++++ 6 files changed, 510 insertions(+), 127 deletions(-) diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index 397e12f..ea85320 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -3016,16 +3016,95 @@ describe("isToolVisibilityAppOnly", () => { expect(app.onteardown).toBe(handler); }); - it("direct setRequestHandler uses base SDK replacement semantics", () => { + it("direct setRequestHandler throws when called twice", () => { const bridge2 = new AppBridge( createMockClient() as Client, testHostInfo, testHostCapabilities, ); - bridge2.setRequestHandler("ping", () => ({})); + const params = z.object({}); + bridge2.setRequestHandler("test/method", { params }, () => ({})); expect(() => { - bridge2.setRequestHandler("ping", () => ({})); + bridge2.setRequestHandler("test/method", { params }, () => ({})); + }).toThrow(/already registered/); + }); + + it("direct setRequestHandler cannot silently replace an on* host handler", () => { + const bridge2 = new AppBridge( + createMockClient() as Client, + testHostInfo, + testHostCapabilities, + ); + bridge2.onopenlink = async () => ({}); + expect(() => { + bridge2.setRequestHandler( + "ui/open-link", + { params: z.object({}) }, + () => ({}), + ); + }).toThrow(/already registered/); + }); + + it("direct setNotificationHandler throws for event-mapped methods", () => { + const bridge2 = new AppBridge( + createMockClient() as Client, + testHostInfo, + testHostCapabilities, + ); + bridge2.onsizechange = () => {}; + expect(() => { + bridge2.setNotificationHandler( + "ui/notifications/size-changed", + { params: z.object({}) }, + () => {}, + ); + }).toThrow(/already registered/); + }); + + it("removeRequestHandler releases the method so an on* setter can re-register", () => { + const bridge2 = new AppBridge( + createMockClient() as Client, + testHostInfo, + testHostCapabilities, + ); + bridge2.removeRequestHandler("tools/call"); + expect(() => { + bridge2.setRequestHandler("tools/call", async () => ({ content: [] })); }).not.toThrow(); + expect(() => { + bridge2.oncalltool = async () => ({ content: [] }); + }).not.toThrow(); + }); + + it("oncreatesamplingmessage has a getter, replace semantics, and a replace warning", () => { + const bridge2 = new AppBridge( + createMockClient() as Client, + testHostInfo, + testHostCapabilities, + ); + expect(bridge2.oncreatesamplingmessage).toBeUndefined(); + const first = async () => ({ + role: "assistant" as const, + content: { type: "text" as const, text: "" }, + model: "m", + }); + bridge2.oncreatesamplingmessage = first; + expect(bridge2.oncreatesamplingmessage).toBe(first); + + const warn = spyOn(console, "warn").mockImplementation(() => {}); + try { + expect(() => { + bridge2.oncreatesamplingmessage = first; + }).not.toThrow(); + expect(warn).toHaveBeenCalledWith( + expect.stringContaining("oncreatesamplingmessage handler replaced"), + ); + } finally { + warn.mockRestore(); + } + + bridge2.oncreatesamplingmessage = undefined; + expect(bridge2.oncreatesamplingmessage).toBeUndefined(); }); }); }); diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 8e48677..04ccb4f 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -34,7 +34,7 @@ import { EmptyResultSchema, LoggingMessageNotificationSchema, } from "@modelcontextprotocol/core"; -import { EventDispatcher } from "./events.js"; +import { EventDispatcher, MethodRegistry } from "./events.js"; import type { ZodLiteral, ZodObject, ZodType } from "zod/v4"; type MethodSchema = ZodObject<{ @@ -42,6 +42,8 @@ type MethodSchema = ZodObject<{ params: ZodType; }>; +type UntypedHandlerSetter = (this: unknown, ...args: unknown[]) => void; + import { type McpUiSandboxResourceReadyNotification, type McpUiSizeChangedNotification, @@ -216,8 +218,8 @@ export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION]; /** * Extra metadata passed to request handlers. * - * This type represents the additional context provided by the base MCP SDK - * `Server` when handling requests. + * This type represents the additional context (`BaseContext`) provided by the + * base MCP SDK `Protocol` when handling requests. * * @internal */ @@ -302,6 +304,81 @@ export class AppBridge extends Protocol { private _initializedReceived = false; private readonly _registeredEvents = new Set(); private readonly _events = new EventDispatcher(); + private readonly _methods = new MethodRegistry(); + + // ── Handler registration with double-set protection ───────────────── + // + // The base SDK `Protocol` silently replaces an existing handler. The four + // overrides below restore the v1 behaviour: a direct `setRequestHandler` / + // `setNotificationHandler` for a method that already has a handler throws, + // so a stray registration cannot silently replace a host's `on*` handler + // (for example the URL allow-listing in `onopenlink`) or disconnect + // `addEventListener` listeners. They are arrow-function class fields rather + // than prototype methods so that `Protocol`'s constructor — which registers + // its own ping/cancelled/progress handlers before our fields initialize — + // hits the base implementation and skips tracking. + + /** + * Registers a request handler. Throws if a handler for the same method has + * already been registered — use the `on*` setter for replace semantics. + * + * @throws {Error} if a handler for this method is already registered. + */ + override setRequestHandler: Protocol["setRequestHandler"] = ( + method: string, + ...rest: unknown[] + ) => { + this._methods.claim(method, "setRequestHandler"); + (super.setRequestHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; + + /** + * Registers a notification handler. Throws if a handler for the same method + * has already been registered — use the `on*` setter (replace semantics) or + * `addEventListener` (multi-listener) for mapped events. + * + * @throws {Error} if a handler for this method is already registered. + */ + override setNotificationHandler: Protocol["setNotificationHandler"] = + (method: string, ...rest: unknown[]) => { + this._methods.claim(method, "setNotificationHandler"); + (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; + + override removeRequestHandler: Protocol["removeRequestHandler"] = + (method: string) => { + this._methods.release(method); + super.removeRequestHandler(method); + }; + + override removeNotificationHandler: Protocol["removeNotificationHandler"] = + (method: string) => { + this._methods.release(method); + super.removeNotificationHandler(method); + }; + + /** + * Register a request handler with replace semantics, bypassing the + * double-set protection of {@link setRequestHandler `setRequestHandler`}. + * Used by the `on*` request-handler setters. + */ + protected replaceRequestHandler: Protocol["setRequestHandler"] = + (method: string, ...rest: unknown[]) => { + this._methods.replace(method); + (super.setRequestHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; /** * The base MCP SDK calls this hook for every standard and custom request @@ -419,7 +496,7 @@ export class AppBridge extends Protocol { * manually using the {@link oncalltool `oncalltool`}, {@link onlistresources `onlistresources`}, etc. setters. * @param _hostInfo - Host application identification (name and version) * @param _hostCapabilities - Features and capabilities the host supports - * @param options - Configuration options (inherited from Server) + * @param options - Configuration options (`ProtocolOptions` from the base MCP SDK plus `hostContext`) * * @example With MCP client (automatic forwarding) * ```ts source="./app-bridge.examples.ts#AppBridge_constructor_withMcpClient" @@ -455,7 +532,7 @@ export class AppBridge extends Protocol { this._hostContext = options?.hostContext || {}; - this.setRequestHandler( + this.replaceRequestHandler( "ui/initialize", { params: McpUiInitializeRequestSchema.shape.params, @@ -464,14 +541,14 @@ export class AppBridge extends Protocol { (params) => this._onAppsInitialize(params), ); - this.setRequestHandler("ping", (request, extra) => { + this.replaceRequestHandler("ping", (request, extra) => { this.onping?.(request.params, extra); return {}; }); // Default handler for requestDisplayMode - returns current mode from host context. // Hosts can override this by setting bridge.onrequestdisplaymode = ... - this.setRequestHandler( + this.replaceRequestHandler( "ui/request-display-mode", { params: McpUiRequestDisplayModeRequestSchema.shape.params, @@ -746,7 +823,7 @@ export class AppBridge extends Protocol { ) { this.warnIfRequestHandlerReplaced("onmessage", this._onmessage, callback); this._onmessage = callback; - this.setRequestHandler( + this.replaceRequestHandler( "ui/message", { params: McpUiMessageRequestSchema.shape.params, @@ -819,7 +896,7 @@ export class AppBridge extends Protocol { ) { this.warnIfRequestHandlerReplaced("onopenlink", this._onopenlink, callback); this._onopenlink = callback; - this.setRequestHandler( + this.replaceRequestHandler( "ui/open-link", { params: McpUiOpenLinkRequestSchema.shape.params, @@ -895,7 +972,7 @@ export class AppBridge extends Protocol { callback, ); this._ondownloadfile = callback; - this.setRequestHandler( + this.replaceRequestHandler( "ui/download-file", { params: McpUiDownloadFileRequestSchema.shape.params, @@ -999,7 +1076,7 @@ export class AppBridge extends Protocol { callback, ); this._onrequestdisplaymode = callback; - this.setRequestHandler( + this.replaceRequestHandler( "ui/request-display-mode", { params: McpUiRequestDisplayModeRequestSchema.shape.params, @@ -1100,7 +1177,7 @@ export class AppBridge extends Protocol { callback, ); this._onupdatemodelcontext = callback; - this.setRequestHandler( + this.replaceRequestHandler( "ui/update-model-context", { params: McpUiUpdateModelContextRequestSchema.shape.params, @@ -1136,8 +1213,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `CallToolRequest` from @modelcontextprotocol/server for the request type - * @see `CallToolResult` from @modelcontextprotocol/server for the result type + * @see `CallToolRequest` from @modelcontextprotocol/client forthe request type + * @see `CallToolResult` from @modelcontextprotocol/client forthe result type */ private _oncalltool?: ( params: CallToolRequest["params"], @@ -1156,7 +1233,7 @@ export class AppBridge extends Protocol { ) { this.warnIfRequestHandlerReplaced("oncalltool", this._oncalltool, callback); this._oncalltool = callback; - this.setRequestHandler("tools/call", async (request, extra) => { + this.replaceRequestHandler("tools/call", async (request, extra) => { if (!this._oncalltool) throw new Error("No oncalltool handler set"); return this._oncalltool(request.params, extra); }); @@ -1190,18 +1267,39 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `CreateMessageRequest` from @modelcontextprotocol/server for the request type - * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/server for result types + * @see `CreateMessageRequest` from @modelcontextprotocol/client forthe request type + * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/client forresult types */ + private _oncreatesamplingmessage?: ( + params: CreateMessageRequest["params"], + extra: RequestHandlerExtra, + ) => Promise; + get oncreatesamplingmessage() { + return this._oncreatesamplingmessage; + } set oncreatesamplingmessage( - callback: ( - params: CreateMessageRequest["params"], - extra: RequestHandlerExtra, - ) => Promise, + callback: + | (( + params: CreateMessageRequest["params"], + extra: RequestHandlerExtra, + ) => Promise) + | undefined, ) { - this.setRequestHandler("sampling/createMessage", async (request, extra) => { - return callback(request.params, extra); - }); + this.warnIfRequestHandlerReplaced( + "oncreatesamplingmessage", + this._oncreatesamplingmessage, + callback, + ); + this._oncreatesamplingmessage = callback; + this.replaceRequestHandler( + "sampling/createMessage", + async (request, extra) => { + if (!this._oncreatesamplingmessage) { + throw new Error("No oncreatesamplingmessage handler set"); + } + return this._oncreatesamplingmessage(request.params, extra); + }, + ); } /** @@ -1221,7 +1319,7 @@ export class AppBridge extends Protocol { * }); * ``` * - * @see `ToolListChangedNotification` from @modelcontextprotocol/server for the notification type + * @see `ToolListChangedNotification` from @modelcontextprotocol/client forthe notification type */ sendToolListChanged(params: ToolListChangedNotification["params"] = {}) { return this.notification({ @@ -1252,8 +1350,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ListResourcesRequest` from @modelcontextprotocol/server for the request type - * @see `ListResourcesResult` from @modelcontextprotocol/server for the result type + * @see `ListResourcesRequest` from @modelcontextprotocol/client forthe request type + * @see `ListResourcesResult` from @modelcontextprotocol/client forthe result type */ private _onlistresources?: ( params: ListResourcesRequest["params"], @@ -1276,7 +1374,7 @@ export class AppBridge extends Protocol { callback, ); this._onlistresources = callback; - this.setRequestHandler("resources/list", async (request, extra) => { + this.replaceRequestHandler("resources/list", async (request, extra) => { if (!this._onlistresources) throw new Error("No onlistresources handler set"); return this._onlistresources(request.params, extra); @@ -1305,8 +1403,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ListResourceTemplatesRequest` from @modelcontextprotocol/server for the request type - * @see `ListResourceTemplatesResult` from @modelcontextprotocol/server for the result type + * @see `ListResourceTemplatesRequest` from @modelcontextprotocol/client forthe request type + * @see `ListResourceTemplatesResult` from @modelcontextprotocol/client forthe result type */ private _onlistresourcetemplates?: ( params: ListResourceTemplatesRequest["params"], @@ -1329,7 +1427,7 @@ export class AppBridge extends Protocol { callback, ); this._onlistresourcetemplates = callback; - this.setRequestHandler( + this.replaceRequestHandler( "resources/templates/list", async (request, extra) => { if (!this._onlistresourcetemplates) @@ -1361,8 +1459,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ReadResourceRequest` from @modelcontextprotocol/server for the request type - * @see `ReadResourceResult` from @modelcontextprotocol/server for the result type + * @see `ReadResourceRequest` from @modelcontextprotocol/client forthe request type + * @see `ReadResourceResult` from @modelcontextprotocol/client forthe result type */ private _onreadresource?: ( params: ReadResourceRequest["params"], @@ -1385,7 +1483,7 @@ export class AppBridge extends Protocol { callback, ); this._onreadresource = callback; - this.setRequestHandler("resources/read", async (request, extra) => { + this.replaceRequestHandler("resources/read", async (request, extra) => { if (!this._onreadresource) throw new Error("No onreadresource handler set"); return this._onreadresource(request.params, extra); @@ -1409,7 +1507,7 @@ export class AppBridge extends Protocol { * }); * ``` * - * @see `ResourceListChangedNotification` from @modelcontextprotocol/server for the notification type + * @see `ResourceListChangedNotification` from @modelcontextprotocol/client forthe notification type */ sendResourceListChanged( params: ResourceListChangedNotification["params"] = {}, @@ -1442,8 +1540,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ListPromptsRequest` from @modelcontextprotocol/server for the request type - * @see `ListPromptsResult` from @modelcontextprotocol/server for the result type + * @see `ListPromptsRequest` from @modelcontextprotocol/client forthe request type + * @see `ListPromptsResult` from @modelcontextprotocol/client forthe result type */ private _onlistprompts?: ( params: ListPromptsRequest["params"], @@ -1466,7 +1564,7 @@ export class AppBridge extends Protocol { callback, ); this._onlistprompts = callback; - this.setRequestHandler("prompts/list", async (request, extra) => { + this.replaceRequestHandler("prompts/list", async (request, extra) => { if (!this._onlistprompts) throw new Error("No onlistprompts handler set"); return this._onlistprompts(request.params, extra); }); @@ -1489,7 +1587,7 @@ export class AppBridge extends Protocol { * }); * ``` * - * @see `PromptListChangedNotification` from @modelcontextprotocol/server for the notification type + * @see `PromptListChangedNotification` from @modelcontextprotocol/client forthe notification type */ sendPromptListChanged(params: PromptListChangedNotification["params"] = {}) { return this.notification({ diff --git a/src/app.test.ts b/src/app.test.ts index 9057710..35a924f 100644 --- a/src/app.test.ts +++ b/src/app.test.ts @@ -248,25 +248,62 @@ describe("App base MCP SDK v2 Protocol migration", () => { }).toThrow(/handler registered after connect/); }); - it("uses base MCP SDK replacement semantics for request handlers", async () => { + it("direct setRequestHandler throws when a handler is already registered", async () => { const pair = await connectPair(); connected.push(pair); + const params = z.object({}); let handledBy = 0; - pair.app.setRequestHandler("ping", () => { + pair.app.setRequestHandler("test/method", { params }, () => { handledBy = 1; return {}; }); - pair.app.setRequestHandler("ping", () => { + expect(() => { + pair.app.setRequestHandler("test/method", { params }, () => { + handledBy = 2; + return {}; + }); + }).toThrow(/already registered/); + + await pair.server.request( + { method: "test/method", params: {} }, + z.object({}), + ); + expect(handledBy).toBe(1); + }); + + it("direct setRequestHandler throws for methods owned by the App itself", async () => { + const pair = await connectPair(); + connected.push(pair); + expect(() => { + pair.app.setRequestHandler("ping", () => ({})); + }).toThrow(/already registered/); + }); + + it("removeRequestHandler releases the method for re-registration", async () => { + const pair = await connectPair(); + connected.push(pair); + const params = z.object({}); + let handledBy = 0; + + pair.app.setRequestHandler("test/method", { params }, () => { + handledBy = 1; + return {}; + }); + pair.app.removeRequestHandler("test/method"); + pair.app.setRequestHandler("test/method", { params }, () => { handledBy = 2; return {}; }); - await pair.server.request({ method: "ping", params: {} }); + await pair.server.request( + { method: "test/method", params: {} }, + z.object({}), + ); expect(handledBy).toBe(2); }); - it("uses base MCP SDK replacement semantics for non-event notifications", async () => { + it("direct setNotificationHandler throws when a handler is already registered", async () => { const pair = await connectPair(); connected.push(pair); const calls: number[] = []; @@ -275,14 +312,70 @@ describe("App base MCP SDK v2 Protocol migration", () => { pair.app.setNotificationHandler("test/notification", { params }, () => { calls.push(1); }); - pair.app.setNotificationHandler("test/notification", { params }, () => { - calls.push(2); - }); + expect(() => { + pair.app.setNotificationHandler("test/notification", { params }, () => { + calls.push(2); + }); + }).toThrow(/already registered/); await pair.server.notification({ method: "test/notification", params: {}, }); - expect(calls).toEqual([2]); + expect(calls).toEqual([1]); + }); + + it("direct setNotificationHandler throws for event-mapped methods (listener first)", () => { + const app = new App( + { name: "view-app", version: "1.0.0" }, + {}, + { autoResize: false }, + ); + app.addEventListener("toolinput", () => {}); + expect(() => { + app.setNotificationHandler( + "ui/notifications/tool-input", + { params: z.object({}) }, + () => {}, + ); + }).toThrow(/already registered/); + }); + + it("event registration throws when a direct handler already owns the method", () => { + const app = new App( + { name: "view-app", version: "1.0.0" }, + {}, + { autoResize: false }, + ); + app.setNotificationHandler( + "ui/notifications/tool-input", + { params: z.object({}) }, + () => {}, + ); + expect(() => { + app.ontoolinput = () => {}; + }).toThrow(/already registered/); + expect(() => { + app.addEventListener("toolinput", () => {}); + }).toThrow(/already registered/); + }); + + it("on* request setters keep replace semantics", async () => { + const pair = await connectPair(); + connected.push(pair); + let handledBy = 0; + pair.app.onteardown = async () => { + handledBy = 1; + return {}; + }; + pair.app.onteardown = async () => { + handledBy = 2; + return {}; + }; + await pair.server.request( + { method: "ui/resource-teardown", params: {} }, + z.object({}), + ); + expect(handledBy).toBe(2); }); }); diff --git a/src/app.ts b/src/app.ts index 3fe84d6..fd6bbc7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -23,8 +23,10 @@ import { } from "@modelcontextprotocol/client"; import { EmptyResultSchema } from "@modelcontextprotocol/core"; export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants.js"; -import { EventDispatcher } from "./events.js"; +import { EventDispatcher, MethodRegistry } from "./events.js"; export { EventDispatcher } from "./events.js"; + +type UntypedHandlerSetter = (this: unknown, ...args: unknown[]) => void; import { PostMessageTransport } from "./message-transport.js"; import { LATEST_PROTOCOL_VERSION, @@ -103,71 +105,6 @@ export { applyDocumentTheme, } from "./styles.js"; -/** - * Metadata key for associating a UI resource URI with a tool. - * - * MCP servers include this key in tool definition metadata (via `tools/list`) - * to indicate which UI resource should be displayed when the tool is called. - * When hosts see a tool with this metadata, they fetch and render the - * corresponding {@link App `App`}. - * - * **Note**: This constant is provided for reference and backwards compatibility. - * Server developers should use {@link server-helpers!registerAppTool `registerAppTool`} - * with the `_meta.ui.resourceUri` format instead. Host developers must check both - * formats for compatibility. - * - * @example Modern format (server-side, not in Apps) - * ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_modernFormat" - * // Preferred: Use registerAppTool with nested ui.resourceUri - * registerAppTool( - * server, - * "weather", - * { - * description: "Get weather forecast", - * _meta: { - * ui: { resourceUri: "ui://weather/forecast" }, - * }, - * }, - * handler, - * ); - * ``` - * - * @example Legacy format (deprecated, for backwards compatibility) - * ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_legacyFormat" - * // Deprecated: Direct use of RESOURCE_URI_META_KEY - * server.registerTool( - * "weather", - * { - * description: "Get weather forecast", - * _meta: { - * [RESOURCE_URI_META_KEY]: "ui://weather/forecast", - * }, - * }, - * handler, - * ); - * ``` - * - * @example How hosts check for this metadata (must support both formats) - * ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_hostSide" - * // Hosts should check both modern and legacy formats - * const meta = tool._meta; - * const uiMeta = meta?.ui as McpUiToolMeta | undefined; - * const legacyUri = meta?.[RESOURCE_URI_META_KEY] as string | undefined; - * const uiUri = uiMeta?.resourceUri ?? legacyUri; - * if (typeof uiUri === "string" && uiUri.startsWith("ui://")) { - * // Fetch the resource and display the UI - * } - * ``` - */ - -/** - * MIME type for MCP UI resources. - * - * Identifies HTML content as an MCP App UI resource. - * - * Used by {@link server-helpers!registerAppResource `registerAppResource`} as the default MIME type for app resources. - */ - /** * Options for configuring {@link App `App`} behavior. * @@ -359,6 +296,80 @@ export class App extends Protocol { private _initializedSent = false; private readonly _registeredEvents = new Set(); private readonly _events = new EventDispatcher(); + private readonly _methods = new MethodRegistry(); + + // ── Handler registration with double-set protection ───────────────── + // + // The base SDK `Protocol` silently replaces an existing handler. The four + // overrides below restore the v1 behaviour: a direct `setRequestHandler` / + // `setNotificationHandler` for a method that already has a handler throws, + // so a stray registration cannot silently disconnect `on*` handlers or + // `addEventListener` listeners. They are arrow-function class fields rather + // than prototype methods so that `Protocol`'s constructor — which registers + // its own ping/cancelled/progress handlers before our fields initialize — + // hits the base implementation and skips tracking. + + /** + * Registers a request handler. Throws if a handler for the same method has + * already been registered — use the `on*` setter for replace semantics. + * + * @throws {Error} if a handler for this method is already registered. + */ + override setRequestHandler: Protocol["setRequestHandler"] = ( + method: string, + ...rest: unknown[] + ) => { + this._methods.claim(method, "setRequestHandler"); + (super.setRequestHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; + + /** + * Registers a notification handler. Throws if a handler for the same method + * has already been registered — use the `on*` setter (replace semantics) or + * `addEventListener` (multi-listener) for mapped events. + * + * @throws {Error} if a handler for this method is already registered. + */ + override setNotificationHandler: Protocol["setNotificationHandler"] = + (method: string, ...rest: unknown[]) => { + this._methods.claim(method, "setNotificationHandler"); + (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; + + override removeRequestHandler: Protocol["removeRequestHandler"] = + (method: string) => { + this._methods.release(method); + super.removeRequestHandler(method); + }; + + override removeNotificationHandler: Protocol["removeNotificationHandler"] = + (method: string) => { + this._methods.release(method); + super.removeNotificationHandler(method); + }; + + /** + * Register a request handler with replace semantics, bypassing the + * double-set protection of {@link setRequestHandler `setRequestHandler`}. + * Used by the `on*` request-handler setters. + */ + protected replaceRequestHandler: Protocol["setRequestHandler"] = + (method: string, ...rest: unknown[]) => { + this._methods.replace(method); + (super.setRequestHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; /** * Warn if a host-bound method is called before {@link connect `connect`} has @@ -524,7 +535,7 @@ export class App extends Protocol { z.config({ jitless: true }); } - this.setRequestHandler("ping", (request) => { + this.replaceRequestHandler("ping", (request) => { console.log("Received ping:", request.params); return {}; }); @@ -1056,7 +1067,7 @@ export class App extends Protocol { ) { this.warnIfRequestHandlerReplaced("onteardown", this._onteardown, callback); this._onteardown = callback; - this.setRequestHandler( + this.replaceRequestHandler( "ui/resource-teardown", { params: McpUiResourceTeardownRequestSchema.shape.params, @@ -1114,7 +1125,7 @@ export class App extends Protocol { ) { this.warnIfRequestHandlerReplaced("oncalltool", this._oncalltool, callback); this._oncalltool = callback; - this.setRequestHandler("tools/call", (request, extra) => { + this.replaceRequestHandler("tools/call", (request, extra) => { if (!this._oncalltool) throw new Error("No oncalltool handler set"); return this._oncalltool(request.params, extra); }); @@ -1185,7 +1196,7 @@ export class App extends Protocol { callback, ); this._onlisttools = callback; - this.setRequestHandler("tools/list", (request, extra) => { + this.replaceRequestHandler("tools/list", (request, extra) => { if (!this._onlisttools) throw new Error("No onlisttools handler set"); return this._onlisttools(request.params, extra); }); @@ -1221,11 +1232,11 @@ export class App extends Protocol { ); } return; - case "ping": - case "ui/resource-teardown": - return; default: - throw new Error(`No handler for method ${method} registered`); + // `ping`, `ui/*`, and custom (vendor-prefixed) methods need no + // declared capability. The base SDK explicitly supports custom + // request handlers via the `{ params, result }` form. + return; } } diff --git a/src/constants.ts b/src/constants.ts index f69cfa5..c879a03 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,66 @@ -/** Legacy metadata key associating an MCP tool with its App resource. */ +/** + * Metadata key for associating a UI resource URI with a tool. + * + * MCP servers include this key in tool definition metadata (via `tools/list`) + * to indicate which UI resource should be displayed when the tool is called. + * When hosts see a tool with this metadata, they fetch and render the + * corresponding {@link app!App `App`}. + * + * **Note**: This constant is provided for reference and backwards compatibility. + * Server developers should use {@link server-helpers!registerAppTool `registerAppTool`} + * with the `_meta.ui.resourceUri` format instead. Host developers must check both + * formats for compatibility. + * + * @example Modern format (server-side, not in Apps) + * ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_modernFormat" + * // Preferred: Use registerAppTool with nested ui.resourceUri + * registerAppTool( + * server, + * "weather", + * { + * description: "Get weather forecast", + * _meta: { + * ui: { resourceUri: "ui://weather/forecast" }, + * }, + * }, + * handler, + * ); + * ``` + * + * @example Legacy format (deprecated, for backwards compatibility) + * ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_legacyFormat" + * // Deprecated: Direct use of RESOURCE_URI_META_KEY + * server.registerTool( + * "weather", + * { + * description: "Get weather forecast", + * _meta: { + * [RESOURCE_URI_META_KEY]: "ui://weather/forecast", + * }, + * }, + * handler, + * ); + * ``` + * + * @example How hosts check for this metadata (must support both formats) + * ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_hostSide" + * // Hosts should check both modern and legacy formats + * const meta = tool._meta; + * const uiMeta = meta?.ui as McpUiToolMeta | undefined; + * const legacyUri = meta?.[RESOURCE_URI_META_KEY] as string | undefined; + * const uiUri = uiMeta?.resourceUri ?? legacyUri; + * if (typeof uiUri === "string" && uiUri.startsWith("ui://")) { + * // Fetch the resource and display the UI + * } + * ``` + */ export const RESOURCE_URI_META_KEY = "ui/resourceUri"; -/** MIME type for MCP App HTML resources. */ +/** + * MIME type for MCP UI resources. + * + * Identifies HTML content as an MCP App UI resource. + * + * Used by {@link server-helpers!registerAppResource `registerAppResource`} as the default MIME type for app resources. + */ export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app"; diff --git a/src/events.ts b/src/events.ts index aff1446..aa2e862 100644 --- a/src/events.ts +++ b/src/events.ts @@ -89,3 +89,44 @@ export class EventDispatcher> { return slot; } } + +/** + * Tracks which JSON-RPC methods already have a handler registered through the + * owning `App` / `AppBridge`, so a second direct `setRequestHandler` / + * `setNotificationHandler` call for the same method throws instead of + * silently replacing the first handler (the base SDK `Protocol` replaces). + * + * The `on*` setters use {@link replace} for DOM-style replace semantics, and + * `removeRequestHandler` / `removeNotificationHandler` use {@link release}. + * + * @internal + */ +export class MethodRegistry { + private readonly _methods = new Set(); + + /** Claim `method`. Throws if a handler is already registered for it. */ + claim(method: string, via: string): void { + if (this._methods.has(method)) { + throw new Error( + `Handler for "${method}" already registered (via ${via}). ` + + `Use addEventListener() to attach multiple listeners, ` + + `or the on* setter for replace semantics.`, + ); + } + this._methods.add(method); + } + + /** Claim `method` with replace semantics (never throws). */ + replace(method: string): void { + this._methods.add(method); + } + + /** Release `method` so it can be claimed again. */ + release(method: string): void { + this._methods.delete(method); + } + + has(method: string): boolean { + return this._methods.has(method); + } +} From f722753c8fa8d85714390c134877722ee4d6800b Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 7 Sep 2026 15:36:30 +0100 Subject: [PATCH 37/45] Finish .js import extensions and drop the zod 3 fallback test Add explicit .js extensions to the relative imports in app.ts, app-bridge.ts and their tests so the emitted declarations resolve under NodeNext (#704). Replace the zod 3 JSON Schema fallback test, whose code path was removed, with the remaining non-Standard-JSON-Schema rejection case as its own test. --- src/app-bridge.test.ts | 35 +++-------------------------------- src/app.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index ea85320..a87b0e7 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -1551,40 +1551,9 @@ describe("App <-> AppBridge integration", () => { ).rejects.toThrow(/Invalid input for tool translate/); }); - it("falls back to z.toJSONSchema for zod schemas lacking ~standard.jsonSchema (zod v3.25.x)", async () => { - // zod v3.25 implements ~standard.validate but not ~standard.jsonSchema. - // Simulate by stripping jsonSchema from a real zod schema. - const v4Schema = z.object({ q: z.string() }); - const zod3LikeSchema = Object.assign(Object.create(v4Schema), { - "~standard": { - version: 1 as const, - vendor: "zod", - validate: v4Schema["~standard"].validate, - types: undefined as - | undefined - | { - readonly input: { q: string }; - readonly output: { q: string }; - }, - // no jsonSchema - }, - }); - + it("rejects listTools when a tool schema does not implement Standard JSON Schema", async () => { const appCapabilities = { tools: { listChanged: true } }; app = new App(testAppInfo, appCapabilities, { autoResize: false }); - app.registerTool( - "search", - { inputSchema: zod3LikeSchema }, - async ({ q }: { q: string }) => ({ - content: [{ type: "text" as const, text: q }], - }), - ); - await app.connect(appTransport); - - const list = await bridge.listTools({}); - expect(list.tools[0].inputSchema.properties).toHaveProperty("q"); - - // Non-zod schema without jsonSchema → listTools rejects with guidance. app.registerTool( "broken", { @@ -1598,6 +1567,8 @@ describe("App <-> AppBridge integration", () => { }, async () => ({ content: [] }), ); + await app.connect(appTransport); + expect(bridge.listTools({})).rejects.toThrow( /does not implement Standard JSON Schema/, ); diff --git a/src/app.test.ts b/src/app.test.ts index 35a924f..8420e81 100644 --- a/src/app.test.ts +++ b/src/app.test.ts @@ -6,13 +6,13 @@ import { } from "@modelcontextprotocol/server"; import { z } from "zod/v4"; -import { App } from "./app"; +import { App } from "./app.js"; import { LATEST_PROTOCOL_VERSION, McpUiInitializeRequestSchema, McpUiInitializeResultSchema, McpUiInitializedNotificationSchema, -} from "./types"; +} from "./types.js"; type ConnectedPair = { app: App; From 6d083cd69f7b8214f236b75cc0ce52fe18617a7a Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 13:09:13 +0100 Subject: [PATCH 38/45] Address review: MCPB build, guard the SDK's own handlers, doc fixes - pdf-server build-mcpb.mjs packs the ext-apps build from this checkout into the staging dir instead of asking the registry for ^2.0.0, which is not published yet; CI's "Build MCPB bundle" step failed on ETARGET. - Claim notifications/cancelled and notifications/progress in the MethodRegistry right after super() so a direct setNotificationHandler cannot silently replace the base Protocol's own handlers. - Migration guide and add-app-to-server skill: custom methods use the three-argument setRequestHandler("m", { params }, handler) form (the two-argument form is only for spec methods); zod schemas live in @modelcontextprotocol/core, not client/server; it is toolInfo.tool.outputSchema that loosened in schema.json, and the recursive JSON-value def is nested under three definitions; core is a required peer that client already depends on. - app-bridge.ts: fix "forthe"/"forresult" typos in @see links, update three JSDoc examples to the method-string setNotificationHandler form, move the UntypedHandlerSetter type below the import block (same in app.ts). - wire-compat.test.ts: say the fixtures are 1.x-shaped, not captures. --- README.md | 2 +- docs/migrate-to-2.md | 24 +++++++---- examples/pdf-server/build-mcpb.mjs | 15 +++++++ .../skills/add-app-to-server/SKILL.md | 5 ++- src/app-bridge.ts | 42 ++++++++++--------- src/app.ts | 7 +++- src/wire-compat.test.ts | 4 +- 7 files changed, 65 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 4b92f0c..acfc661 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ resources: ## Getting Started Requires Node.js 20+. The base MCP SDK packages are `^2.0.0` peers of -`ext-apps` (`@modelcontextprotocol/core` is pulled in transitively). +`ext-apps` (`@modelcontextprotocol/core` is a required peer that `client` already depends on, so npm installs it for you). For a View or host: diff --git a/docs/migrate-to-2.md b/docs/migrate-to-2.md index 3f6db7b..2164b83 100644 --- a/docs/migrate-to-2.md +++ b/docs/migrate-to-2.md @@ -27,7 +27,7 @@ host-side deltas are in error responses (see below). | CDN / `*-with-deps` | nothing extra: `./app-with-deps` and `./react-with-deps` bundle client, core and zod (about 25% larger than the 1.x bundles) | `@modelcontextprotocol/client` is a required peer (`App` and `AppBridge` extend -its `Protocol` class); `@modelcontextprotocol/core` is installed transitively; +its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `client` already depends on, so npm installs it without you listing it; `@modelcontextprotocol/server` stays optional and is only needed for the `./server` helpers. Node.js 20+ is required. @@ -48,8 +48,10 @@ its `Protocol` class); `@modelcontextprotocol/core` is installed transitively; `extra.mcpReq.id`. - **`setRequestHandler` / `setNotificationHandler` take method names.** `app.setRequestHandler(SomeRequestSchema, handler)` becomes - `app.setRequestHandler("some/method", handler)` (pass the params schema as the - optional extra argument for validation). + `app.setRequestHandler("some/method", { params: SomeParamsSchema }, (params, ctx) => …)` + for custom methods (the handler receives the parsed params); the two-argument + `setRequestHandler("tools/call", handler)` form exists only for spec-defined + method names. - **Errors.** Remote JSON-RPC errors are `ProtocolError` (numeric `code`); local conditions are `SdkError` with a string `code`: request timeout → `"REQUEST_TIMEOUT"`, connection closed → `"CONNECTION_CLOSED"`. Cancelling a @@ -74,10 +76,15 @@ The published `./schema.json` export is regenerated from the 2.x core schemas: - `McpUiToolResultNotification.params.structuredContent` is any JSON value (was `type: "object"`). -- Result `_meta` documents `io.modelcontextprotocol/serverInfo` and no longer - lists `progressToken` / `related-task` (both still pass through). -- `McpUiHostContext.toolInfo.tool.inputSchema` is a loose object. -- A recursive `$defs.__schema0` JSON-value definition is added. +- `McpUiToolResultNotification.params._meta` documents + `io.modelcontextprotocol/serverInfo` and no longer lists `progressToken` / + `related-task` (both still pass through). +- `McpUiHostContext.toolInfo.tool.outputSchema` is a loose object (only + `$schema` is documented); `inputSchema.properties` values are now typed as + JSON values. +- A recursive JSON-value definition (`__schema0`) is added under the `$defs` of + `McpUiHostContext`, `McpUiHostContextChangedNotification` and + `McpUiInitializeResult`. ## Checklist @@ -87,7 +94,8 @@ The published `./schema.json` export is regenerated from the 2.x core schemas: `@modelcontextprotocol/server`, `sdk/server/streamableHttp.js` → `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node`, `sdk/server/stdio.js` → `@modelcontextprotocol/server/stdio`, `sdk/types.js` - → `@modelcontextprotocol/client` or `server`). + → `@modelcontextprotocol/client` or `server` for the types, + `@modelcontextprotocol/core` for the zod schemas). 3. Wrap raw zod shapes with `z.object({...})`. 4. Update custom handlers to the `extra.mcpReq.*` context and method-keyed `setRequestHandler` calls. diff --git a/examples/pdf-server/build-mcpb.mjs b/examples/pdf-server/build-mcpb.mjs index c1c3469..2917334 100644 --- a/examples/pdf-server/build-mcpb.mjs +++ b/examples/pdf-server/build-mcpb.mjs @@ -43,6 +43,21 @@ writeFileSync( path.join(stage, "manifest.json"), JSON.stringify(manifest, null, 2), ); +// The bundle must ship the ext-apps build from this checkout, not whatever +// the registry has: pack the repo root and point the staged manifest at it. +const repoRoot = path.resolve(here, "..", ".."); +const packed = JSON.parse( + execSync( + "npm pack --json --ignore-scripts --pack-destination " + + JSON.stringify(stage), + { + cwd: repoRoot, + encoding: "utf8", + }, + ).replace(/^[^[]*/, ""), +); +pkg.dependencies["@modelcontextprotocol/ext-apps"] = + "file:./" + packed[0].filename; writeFileSync(path.join(stage, "package.json"), JSON.stringify(pkg, null, 2)); const run = (cmd) => execSync(cmd, { cwd: stage, stdio: "inherit" }); diff --git a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md index df7d931..5a473f0 100644 --- a/plugins/mcp-apps/skills/add-app-to-server/SKILL.md +++ b/plugins/mcp-apps/skills/add-app-to-server/SKILL.md @@ -94,10 +94,11 @@ handler schemas before adding the App integration: | `sdk/server/streamableHttp.js` (`StreamableHTTPServerTransport`) | `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node` | | Express wiring by hand | `createMcpExpressApp` from `@modelcontextprotocol/express` | | `sdk/server/stdio.js` | `@modelcontextprotocol/server/stdio` | -| `sdk/types.js` (types, schemas) | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | +| `sdk/types.js` types (`CallToolResult`, …) | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | +| `sdk/types.js` zod schemas (`CallToolResultSchema`, …) | `@modelcontextprotocol/core` | | Raw zod shapes: `inputSchema: { q: z.string() }` | `inputSchema: z.object({ q: z.string() })` | | `extra.signal` in tool callbacks | `extra.mcpReq.signal` | -| `setRequestHandler(SomeRequestSchema, handler)` | `setRequestHandler("some/method", handler)` | +| `setRequestHandler(SomeRequestSchema, handler)` | `setRequestHandler("some/method", { params: ParamsSchema }, handler)` (the 2-arg form is only for spec methods such as `"tools/call"`) | ## Step 3: Set Up the Build Pipeline diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 04ccb4f..3d145d4 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -42,8 +42,6 @@ type MethodSchema = ZodObject<{ params: ZodType; }>; -type UntypedHandlerSetter = (this: unknown, ...args: unknown[]) => void; - import { type McpUiSandboxResourceReadyNotification, type McpUiSizeChangedNotification, @@ -93,6 +91,8 @@ import { export * from "./types.js"; export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./constants.js"; import { RESOURCE_URI_META_KEY } from "./constants.js"; + +type UntypedHandlerSetter = (this: unknown, ...args: unknown[]) => void; export { PostMessageTransport } from "./message-transport.js"; /** @@ -527,6 +527,8 @@ export class AppBridge extends Protocol { options?: HostOptions, ) { super(options); + this._methods.replace("notifications/cancelled"); + this._methods.replace("notifications/progress"); this._ensureEventSlot("initialized"); @@ -1213,8 +1215,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `CallToolRequest` from @modelcontextprotocol/client forthe request type - * @see `CallToolResult` from @modelcontextprotocol/client forthe result type + * @see `CallToolRequest` from @modelcontextprotocol/client for the request type + * @see `CallToolResult` from @modelcontextprotocol/client for the result type */ private _oncalltool?: ( params: CallToolRequest["params"], @@ -1267,8 +1269,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `CreateMessageRequest` from @modelcontextprotocol/client forthe request type - * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/client forresult types + * @see `CreateMessageRequest` from @modelcontextprotocol/client for the request type + * @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/client for result types */ private _oncreatesamplingmessage?: ( params: CreateMessageRequest["params"], @@ -1314,12 +1316,12 @@ export class AppBridge extends Protocol { * @example * ```typescript * // In your MCP client notification handler: - * mcpClient.setNotificationHandler(ToolListChangedNotificationSchema, () => { + * mcpClient.setNotificationHandler("notifications/tools/list_changed", () => { * bridge.sendToolListChanged(); * }); * ``` * - * @see `ToolListChangedNotification` from @modelcontextprotocol/client forthe notification type + * @see `ToolListChangedNotification` from @modelcontextprotocol/client for the notification type */ sendToolListChanged(params: ToolListChangedNotification["params"] = {}) { return this.notification({ @@ -1350,8 +1352,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ListResourcesRequest` from @modelcontextprotocol/client forthe request type - * @see `ListResourcesResult` from @modelcontextprotocol/client forthe result type + * @see `ListResourcesRequest` from @modelcontextprotocol/client for the request type + * @see `ListResourcesResult` from @modelcontextprotocol/client for the result type */ private _onlistresources?: ( params: ListResourcesRequest["params"], @@ -1403,8 +1405,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ListResourceTemplatesRequest` from @modelcontextprotocol/client forthe request type - * @see `ListResourceTemplatesResult` from @modelcontextprotocol/client forthe result type + * @see `ListResourceTemplatesRequest` from @modelcontextprotocol/client for the request type + * @see `ListResourceTemplatesResult` from @modelcontextprotocol/client for the result type */ private _onlistresourcetemplates?: ( params: ListResourceTemplatesRequest["params"], @@ -1459,8 +1461,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ReadResourceRequest` from @modelcontextprotocol/client forthe request type - * @see `ReadResourceResult` from @modelcontextprotocol/client forthe result type + * @see `ReadResourceRequest` from @modelcontextprotocol/client for the request type + * @see `ReadResourceResult` from @modelcontextprotocol/client for the result type */ private _onreadresource?: ( params: ReadResourceRequest["params"], @@ -1502,12 +1504,12 @@ export class AppBridge extends Protocol { * @example * ```typescript * // In your MCP client notification handler: - * mcpClient.setNotificationHandler(ResourceListChangedNotificationSchema, () => { + * mcpClient.setNotificationHandler("notifications/resources/list_changed", () => { * bridge.sendResourceListChanged(); * }); * ``` * - * @see `ResourceListChangedNotification` from @modelcontextprotocol/client forthe notification type + * @see `ResourceListChangedNotification` from @modelcontextprotocol/client for the notification type */ sendResourceListChanged( params: ResourceListChangedNotification["params"] = {}, @@ -1540,8 +1542,8 @@ export class AppBridge extends Protocol { * }; * ``` * - * @see `ListPromptsRequest` from @modelcontextprotocol/client forthe request type - * @see `ListPromptsResult` from @modelcontextprotocol/client forthe result type + * @see `ListPromptsRequest` from @modelcontextprotocol/client for the request type + * @see `ListPromptsResult` from @modelcontextprotocol/client for the result type */ private _onlistprompts?: ( params: ListPromptsRequest["params"], @@ -1582,12 +1584,12 @@ export class AppBridge extends Protocol { * @example * ```typescript * // In your MCP client notification handler: - * mcpClient.setNotificationHandler(PromptListChangedNotificationSchema, () => { + * mcpClient.setNotificationHandler("notifications/prompts/list_changed", () => { * bridge.sendPromptListChanged(); * }); * ``` * - * @see `PromptListChangedNotification` from @modelcontextprotocol/client forthe notification type + * @see `PromptListChangedNotification` from @modelcontextprotocol/client for the notification type */ sendPromptListChanged(params: PromptListChangedNotification["params"] = {}) { return this.notification({ diff --git a/src/app.ts b/src/app.ts index fd6bbc7..58ea9da 100644 --- a/src/app.ts +++ b/src/app.ts @@ -26,7 +26,6 @@ export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants.js"; import { EventDispatcher, MethodRegistry } from "./events.js"; export { EventDispatcher } from "./events.js"; -type UntypedHandlerSetter = (this: unknown, ...args: unknown[]) => void; import { PostMessageTransport } from "./message-transport.js"; import { LATEST_PROTOCOL_VERSION, @@ -69,6 +68,8 @@ import { } from "./standard-schema.js"; import { z, type ZodLiteral, type ZodObject, type ZodType } from "zod/v4"; +type UntypedHandlerSetter = (this: unknown, ...args: unknown[]) => void; + type MethodSchema = ZodObject<{ method: ZodLiteral; params: ZodType; @@ -530,6 +531,10 @@ export class App extends Protocol { private options: AppOptions = { autoResize: true }, ) { super(options); + // Claim the handlers the base Protocol constructor installed so a direct + // setNotificationHandler cannot silently replace them either. + this._methods.replace("notifications/cancelled"); + this._methods.replace("notifications/progress"); if (!options.allowUnsafeEval) { z.config({ jitless: true }); diff --git a/src/wire-compat.test.ts b/src/wire-compat.test.ts index 5fdca8b..762ac77 100644 --- a/src/wire-compat.test.ts +++ b/src/wire-compat.test.ts @@ -77,7 +77,7 @@ const hostContext: McpUiHostContext = { locale: "en-US", }; -/** Captured from ext-apps 1.7.x View (sdk 1.x key order, numeric ids from 0). */ +/** Shaped like ext-apps 1.7.x View traffic (sdk-1 key order, 0-based numeric ids); not a recorded capture. */ const v1View = { initialize: { method: "ui/initialize", @@ -120,7 +120,7 @@ const v1View = { }), }; -/** Captured from ext-apps 1.7.x AppBridge (sdk 1.x). */ +/** Shaped like ext-apps 1.7.x AppBridge traffic (sdk 1.x); not a recorded capture. */ const v1Host = { initializeResult: (id: number) => ({ result: { From 728b0bf38ece48aa636a39be4fa3f44deaead010 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 14:24:25 +0100 Subject: [PATCH 39/45] Keep the 1.x handler registration forms as deprecated overloads Consumers on 1.x wrote setRequestHandler(Schema, handler) and setNotificationHandler(Schema, handler) with a whole-message Zod schema. SDK 2.0 keys handlers by method name, so App and AppBridge now accept that form as a third, deprecated overload: a small helper detects the schema, extracts the method literal, forwards to the 2.x (method, { params }, handler) form, and hands the legacy handler the reassembled { method, params } message plus a 1.x-shaped extra (signal, requestId, sessionId, _meta). A one-time console warning points at the new form. The double-registration guard applies to both forms. The AppRequest, AppNotification and AppResult unions that 1.x fed to the SDK's Protocol generics come back as deprecated type aliases, since they leaked through the root entry and cost nothing to keep. Both go away in 3.0. --- docs/migrate-to-2.md | 11 ++- src/app-bridge.ts | 42 +++++++---- src/app.ts | 42 +++++++---- src/legacy-handlers.test.ts | 145 ++++++++++++++++++++++++++++++++++++ src/legacy-handlers.ts | 94 +++++++++++++++++++++++ src/types.ts | 117 +++++++++++++++++++++++++++++ 6 files changed, 420 insertions(+), 31 deletions(-) create mode 100644 src/legacy-handlers.test.ts create mode 100644 src/legacy-handlers.ts diff --git a/docs/migrate-to-2.md b/docs/migrate-to-2.md index 2164b83..515c363 100644 --- a/docs/migrate-to-2.md +++ b/docs/migrate-to-2.md @@ -41,8 +41,9 @@ its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `cli Valibot. Raw zod shapes (`{ q: z.string() }`) still work with `registerAppTool` but are deprecated; wrap them with `z.object({...})`. - **`App` / `AppBridge` extend `Protocol` from `@modelcontextprotocol/client`.** - `ProtocolWithEvents`, `AppRequest`, `AppNotification` and `AppResult` are - gone; use the SDK's `Protocol`, `Request`, `Notification` and `Result`. + `ProtocolWithEvents` is gone; use the SDK's `Protocol`. The `AppRequest`, + `AppNotification` and `AppResult` unions are kept as deprecated type + aliases (nothing in 2.x consumes them) and will be removed in 3.0. - **Handler context.** Custom handlers receive the SDK 2.x context: `extra.signal` → `extra.mcpReq.signal`, `extra.requestId` → `extra.mcpReq.id`. @@ -51,7 +52,11 @@ its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `cli `app.setRequestHandler("some/method", { params: SomeParamsSchema }, (params, ctx) => …)` for custom methods (the handler receives the parsed params); the two-argument `setRequestHandler("tools/call", handler)` form exists only for spec-defined - method names. + method names. The 1.x `(Schema, handler)` form still works on `App` and + `AppBridge` as a deprecated overload: it logs a one-time warning, hands the + handler the whole `{ method, params }` message as before, and gives request + handlers a 1.x-shaped `extra` (`signal`, `requestId`, `sessionId`, `_meta`). + It will be removed in 3.0. - **Errors.** Remote JSON-RPC errors are `ProtocolError` (numeric `code`); local conditions are `SdkError` with a string `code`: request timeout → `"REQUEST_TIMEOUT"`, connection closed → `"CONNECTION_CLOSED"`. Cancelling a diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 3d145d4..c61dfa4 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -35,6 +35,12 @@ import { LoggingMessageNotificationSchema, } from "@modelcontextprotocol/core"; import { EventDispatcher, MethodRegistry } from "./events.js"; +import { + toModernArgs, + type LegacyMethodSchema, + type LegacyNotificationHandler, + type LegacyRequestHandler, +} from "./legacy-handlers.js"; import type { ZodLiteral, ZodObject, ZodType } from "zod/v4"; type MethodSchema = ZodObject<{ @@ -324,11 +330,14 @@ export class AppBridge extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setRequestHandler: Protocol["setRequestHandler"] = ( - method: string, - ...rest: unknown[] - ) => { - this._methods.claim(method, "setRequestHandler"); + override setRequestHandler: Protocol["setRequestHandler"] & + (( + /** @deprecated Pass the method name and `{ params }` instead. */ + schema: S, + handler: LegacyRequestHandler, + ) => void) = (...args: unknown[]) => { + const [method, ...rest] = toModernArgs("request", args) ?? args; + this._methods.claim(method as string, "setRequestHandler"); (super.setRequestHandler as unknown as UntypedHandlerSetter).call( this, method, @@ -343,15 +352,20 @@ export class AppBridge extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setNotificationHandler: Protocol["setNotificationHandler"] = - (method: string, ...rest: unknown[]) => { - this._methods.claim(method, "setNotificationHandler"); - (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( - this, - method, - ...rest, - ); - }; + override setNotificationHandler: Protocol["setNotificationHandler"] & + (( + /** @deprecated Pass the method name and `{ params }` instead. */ + schema: S, + handler: LegacyNotificationHandler, + ) => void) = (...args: unknown[]) => { + const [method, ...rest] = toModernArgs("notification", args) ?? args; + this._methods.claim(method as string, "setNotificationHandler"); + (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; override removeRequestHandler: Protocol["removeRequestHandler"] = (method: string) => { diff --git a/src/app.ts b/src/app.ts index 58ea9da..da27b43 100644 --- a/src/app.ts +++ b/src/app.ts @@ -24,6 +24,12 @@ import { import { EmptyResultSchema } from "@modelcontextprotocol/core"; export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants.js"; import { EventDispatcher, MethodRegistry } from "./events.js"; +import { + toModernArgs, + type LegacyMethodSchema, + type LegacyNotificationHandler, + type LegacyRequestHandler, +} from "./legacy-handlers.js"; export { EventDispatcher } from "./events.js"; import { PostMessageTransport } from "./message-transport.js"; @@ -316,11 +322,14 @@ export class App extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setRequestHandler: Protocol["setRequestHandler"] = ( - method: string, - ...rest: unknown[] - ) => { - this._methods.claim(method, "setRequestHandler"); + override setRequestHandler: Protocol["setRequestHandler"] & + (( + /** @deprecated Pass the method name and `{ params }` instead. */ + schema: S, + handler: LegacyRequestHandler, + ) => void) = (...args: unknown[]) => { + const [method, ...rest] = toModernArgs("request", args) ?? args; + this._methods.claim(method as string, "setRequestHandler"); (super.setRequestHandler as unknown as UntypedHandlerSetter).call( this, method, @@ -335,15 +344,20 @@ export class App extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setNotificationHandler: Protocol["setNotificationHandler"] = - (method: string, ...rest: unknown[]) => { - this._methods.claim(method, "setNotificationHandler"); - (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( - this, - method, - ...rest, - ); - }; + override setNotificationHandler: Protocol["setNotificationHandler"] & + (( + /** @deprecated Pass the method name and `{ params }` instead. */ + schema: S, + handler: LegacyNotificationHandler, + ) => void) = (...args: unknown[]) => { + const [method, ...rest] = toModernArgs("notification", args) ?? args; + this._methods.claim(method as string, "setNotificationHandler"); + (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( + this, + method, + ...rest, + ); + }; override removeRequestHandler: Protocol["removeRequestHandler"] = (method: string) => { diff --git a/src/legacy-handlers.test.ts b/src/legacy-handlers.test.ts new file mode 100644 index 0000000..0ef36d6 --- /dev/null +++ b/src/legacy-handlers.test.ts @@ -0,0 +1,145 @@ +import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"; +import { InMemoryTransport, Server } from "@modelcontextprotocol/server"; +import { z } from "zod/v4"; + +import { App } from "./app.js"; +import { AppBridge } from "./app-bridge.js"; +import { + McpUiInitializeRequestSchema, + McpUiInitializeResultSchema, + McpUiInitializedNotificationSchema, + McpUiResourceTeardownRequestSchema, + McpUiToolInputNotificationSchema, + McpUiSizeChangedNotificationSchema, + McpUiOpenLinkRequestSchema, +} from "./types.js"; + +const CustomRequestSchema = z.object({ + method: z.literal("test/echo"), + params: z.object({ text: z.string() }), +}); + +const CustomNotificationSchema = z.object({ + method: z.literal("test/ping"), + params: z.object({ n: z.number() }), +}); + +let warn: ReturnType; +beforeEach(() => { + warn = spyOn(console, "warn").mockImplementation(() => {}); +}); +afterEach(() => warn.mockRestore()); + +describe("1.x setRequestHandler(Schema, handler) on App", () => { + it("registers a custom request and hands the handler the whole message plus a 1.x extra", async () => { + const app = new App({ name: "a", version: "1" }, {}, { autoResize: false }); + const server = new Server( + { name: "s", version: "1" }, + { capabilities: {} }, + ); + server.setRequestHandler( + "ui/initialize", + { + params: McpUiInitializeRequestSchema.shape.params, + result: McpUiInitializeResultSchema, + }, + () => ({ + protocolVersion: "2026-01-26", + hostCapabilities: {}, + hostInfo: { name: "h", version: "1" }, + hostContext: {}, + }), + ); + server.setNotificationHandler( + "ui/notifications/initialized", + { params: McpUiInitializedNotificationSchema.shape.params }, + () => {}, + ); + + let seen: unknown; + let seenExtra: { signal: AbortSignal; requestId: unknown } | undefined; + app.setRequestHandler(CustomRequestSchema, (request, extra) => { + seen = request; + seenExtra = extra; + return { echoed: request.params.text }; + }); + + const [at, st] = InMemoryTransport.createLinkedPair(); + await server.connect(st); + await app.connect(at); + + const result = await server.request( + { method: "test/echo", params: { text: "hi" } }, + z.object({ echoed: z.string() }), + ); + + expect(result).toEqual({ echoed: "hi" }); + expect(seen).toEqual({ method: "test/echo", params: { text: "hi" } }); + expect(seenExtra?.signal).toBeInstanceOf(AbortSignal); + expect(seenExtra?.requestId).toBeDefined(); + expect(warn).toHaveBeenCalledTimes(1); + expect(String(warn.mock.calls[0][0])).toContain( + 'setRequestHandler("test/echo", { params: Schema.shape.params }, handler)', + ); + + await app.close(); + await server.close(); + }); + + it("still throws for a method an on* setter already owns", () => { + const app = new App({ name: "a", version: "1" }, {}, { autoResize: false }); + app.onteardown = async () => ({}); + expect(() => + app.setRequestHandler( + McpUiResourceTeardownRequestSchema, + async () => ({}), + ), + ).toThrow(/already registered/); + }); + + it("still throws for an event-mapped notification", () => { + const app = new App({ name: "a", version: "1" }, {}, { autoResize: false }); + app.ontoolinput = () => {}; + expect(() => + app.setNotificationHandler(McpUiToolInputNotificationSchema, () => {}), + ).toThrow(/already registered/); + }); +}); + +describe("1.x setNotificationHandler(Schema, handler) on AppBridge", () => { + it("registers a custom notification and hands the handler the whole message", async () => { + const bridge = new AppBridge(null, { name: "h", version: "1" }, {}); + // A bare Server stands in for the View: unlike Client, connect() sends no + // MCP initialize, matching what a real App does on this channel. + const view = new Server({ name: "v", version: "1" }, { capabilities: {} }); + let seen: unknown; + bridge.setNotificationHandler(CustomNotificationSchema, (n) => { + seen = n; + }); + + const [bt, vt] = InMemoryTransport.createLinkedPair(); + await bridge.connect(bt); + await view.connect(vt); + await view.notification({ method: "test/ping", params: { n: 3 } }); + await new Promise((r) => setTimeout(r, 10)); + + expect(seen).toEqual({ method: "test/ping", params: { n: 3 } }); + await view.close(); + await bridge.close(); + }); + + it("still throws for a method an on* setter already owns", () => { + const bridge = new AppBridge(null, { name: "h", version: "1" }, {}); + bridge.onsizechange = () => {}; + expect(() => + bridge.setNotificationHandler( + McpUiSizeChangedNotificationSchema, + () => {}, + ), + ).toThrow(/already registered/); + bridge.onopenlink = async () => ({}); + expect(() => + bridge.setRequestHandler(McpUiOpenLinkRequestSchema, async () => ({})), + ).toThrow(/already registered/); + }); +}); diff --git a/src/legacy-handlers.ts b/src/legacy-handlers.ts new file mode 100644 index 0000000..b4d1157 --- /dev/null +++ b/src/legacy-handlers.ts @@ -0,0 +1,94 @@ +import type { BaseContext, RequestId } from "@modelcontextprotocol/client"; +import type { ZodLiteral, ZodObject, ZodType } from "zod/v4"; + +/** + * A whole-message Zod schema (`method` literal plus `params`), as accepted by + * the 1.x `setRequestHandler(Schema, handler)` forms. + */ +export type LegacyMethodSchema = ZodObject<{ + method: ZodLiteral; + params: ZodType; +}>; + +/** + * The `extra` argument 1.x request handlers received. + * + * @deprecated Use the 2.x `BaseContext` (`extra.mcpReq.signal`, `extra.mcpReq.id`). + */ +export type LegacyRequestHandlerExtra = { + signal: AbortSignal; + requestId: RequestId; + sessionId?: string; + _meta?: BaseContext["mcpReq"]["_meta"]; + sendRequest: BaseContext["mcpReq"]["send"]; + sendNotification: BaseContext["mcpReq"]["notify"]; + authInfo?: NonNullable["authInfo"]; +}; + +/** @deprecated Use `setRequestHandler("method", { params, result }, (params, ctx) => …)`. */ +export type LegacyRequestHandler = ( + request: S["_output"], + extra: LegacyRequestHandlerExtra, +) => unknown; + +/** @deprecated Use `setNotificationHandler("method", { params }, (params) => …)`. */ +export type LegacyNotificationHandler = ( + notification: S["_output"], +) => void | Promise; + +/** + * Arguments for the 2.x three-argument `setRequestHandler` / + * `setNotificationHandler` form. + */ +type ModernArgs = [ + method: string, + schemas: { params: ZodType }, + handler: (params: unknown, ctx: BaseContext) => unknown, +]; + +const warned = new Set(); + +/** + * Translate a 1.x `(Schema, handler)` registration into the 2.x + * `(method, { params }, handler)` form, or return `undefined` when the call + * is already in the 2.x form. The 1.x handler receives the reassembled + * `{ method, params }` message and, for requests, a 1.x-shaped `extra`. + */ +export function toModernArgs( + kind: "request" | "notification", + args: unknown[], +): ModernArgs | undefined { + const [first, handler] = args; + const shape = (first as Partial | undefined)?.shape; + if (typeof shape?.method?.value !== "string") return undefined; + const method = shape.method.value; + const setter = + kind === "request" ? "setRequestHandler" : "setNotificationHandler"; + if (!warned.has(setter)) { + warned.add(setter); + console.warn( + `[ext-apps] ${setter}(Schema, handler) is deprecated; use ` + + `${setter}("${method}", { params: Schema.shape.params }, handler).`, + ); + } + const call = handler as (...a: unknown[]) => unknown; + return [ + method, + { params: shape.params }, + kind === "request" + ? (params, ctx) => call({ method, params }, legacyExtra(ctx)) + : (params) => call({ method, params }), + ]; +} + +function legacyExtra(ctx: BaseContext): LegacyRequestHandlerExtra { + return { + signal: ctx.mcpReq.signal, + requestId: ctx.mcpReq.id, + sessionId: ctx.sessionId, + _meta: ctx.mcpReq._meta, + sendRequest: ctx.mcpReq.send, + sendNotification: ctx.mcpReq.notify, + authInfo: ctx.http?.authInfo, + }; +} diff --git a/src/types.ts b/src/types.ts index 655812b..e4ed629 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,55 @@ * @see `generated/schema.test.ts` for compile-time verification */ +import type { + CallToolRequest, + CallToolResult, + CreateMessageRequest, + CreateMessageResult, + CreateMessageResultWithTools, + EmptyResult, + ListPromptsRequest, + ListPromptsResult, + ListResourceTemplatesRequest, + ListResourceTemplatesResult, + ListResourcesRequest, + ListResourcesResult, + ListToolsRequest, + ListToolsResult, + LoggingMessageNotification, + PingRequest, + PromptListChangedNotification, + ReadResourceRequest, + ReadResourceResult, + ResourceListChangedNotification, + ToolListChangedNotification, +} from "@modelcontextprotocol/client"; +import type { + McpUiDownloadFileRequest, + McpUiDownloadFileResult, + McpUiHostContextChangedNotification, + McpUiInitializeRequest, + McpUiInitializeResult, + McpUiInitializedNotification, + McpUiMessageRequest, + McpUiMessageResult, + McpUiOpenLinkRequest, + McpUiOpenLinkResult, + McpUiRequestDisplayModeRequest, + McpUiRequestDisplayModeResult, + McpUiRequestTeardownNotification, + McpUiResourceTeardownRequest, + McpUiResourceTeardownResult, + McpUiSandboxProxyReadyNotification, + McpUiSandboxResourceReadyNotification, + McpUiSizeChangedNotification, + McpUiToolCancelledNotification, + McpUiToolInputNotification, + McpUiToolInputPartialNotification, + McpUiToolResultNotification, + McpUiUpdateModelContextRequest, +} from "./spec.types.js"; + // Re-export all types from spec.types.ts export { LATEST_PROTOCOL_VERSION, @@ -108,3 +157,71 @@ export { McpUiToolVisibilitySchema, McpUiToolMetaSchema, } from "./generated/schema.js"; + +/** + * Union of every request an {@link app!App `App`} may send or receive. + * + * @deprecated 1.x fed this to the SDK's `Protocol` generics; SDK 2.x derives + * request types from the method name, so nothing consumes it anymore. + */ +export type AppRequest = + | McpUiInitializeRequest + | McpUiOpenLinkRequest + | McpUiDownloadFileRequest + | McpUiMessageRequest + | McpUiUpdateModelContextRequest + | McpUiResourceTeardownRequest + | McpUiRequestDisplayModeRequest + | CallToolRequest + | ListToolsRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | ListPromptsRequest + | CreateMessageRequest + | PingRequest; + +/** + * Union of every notification an {@link app!App `App`} may send or receive. + * + * @deprecated See {@link AppRequest}. + */ +export type AppNotification = + // Sent to app + | McpUiHostContextChangedNotification + | McpUiToolInputNotification + | McpUiToolInputPartialNotification + | McpUiToolResultNotification + | McpUiToolCancelledNotification + | McpUiSandboxResourceReadyNotification + | ToolListChangedNotification + | ResourceListChangedNotification + | PromptListChangedNotification + // Received from app + | McpUiInitializedNotification + | McpUiSizeChangedNotification + | McpUiSandboxProxyReadyNotification + | McpUiRequestTeardownNotification + | LoggingMessageNotification; + +/** + * Union of every result an {@link app!App `App`} may send or receive. + * + * @deprecated See {@link AppRequest}. + */ +export type AppResult = + | McpUiInitializeResult + | McpUiOpenLinkResult + | McpUiDownloadFileResult + | McpUiMessageResult + | McpUiResourceTeardownResult + | McpUiRequestDisplayModeResult + | CallToolResult + | ListToolsResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | ListPromptsResult + | CreateMessageResult + | CreateMessageResultWithTools + | EmptyResult; From 4eab52e372e064f4a58dfcb24040df9155564f7d Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 15:31:03 +0100 Subject: [PATCH 40/45] Export the legacy handler types from the root entry They appear in App's public setRequestHandler / setNotificationHandler signatures, so TypeDoc's link validation needs them reachable from the entry point. --- src/app.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app.ts b/src/app.ts index da27b43..0ab51ac 100644 --- a/src/app.ts +++ b/src/app.ts @@ -30,6 +30,12 @@ import { type LegacyNotificationHandler, type LegacyRequestHandler, } from "./legacy-handlers.js"; +export type { + LegacyMethodSchema, + LegacyNotificationHandler, + LegacyRequestHandler, + LegacyRequestHandlerExtra, +} from "./legacy-handlers.js"; export { EventDispatcher } from "./events.js"; import { PostMessageTransport } from "./message-transport.js"; From 5f6346ef8662c17b25f6edec1c9719b19413fd77 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 14:34:47 +0100 Subject: [PATCH 41/45] Add cross-version interop test against published ext-apps 1.7.5 Install @modelcontextprotocol/ext-apps@1.7.5 under the npm alias ext-apps-v1 (plus @modelcontextprotocol/sdk@1.30.0, its peer) as devDependencies and run the 2.x App against the 1.x AppBridge and vice versa over an in-memory duplex, with each bridge fronting a real Client+McpServer of its own SDK generation. Covers initialize, notifications, view-to-host requests, tool/resource proxying, error codes, AbortSignal cancellation, teardown and close. --- package-lock.json | 198 ++++++++++++++++++++- package.json | 3 + src/interop-v1.test.ts | 379 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 src/interop-v1.test.ts diff --git a/package-lock.json b/package-lock.json index f63b3d8..0189ca2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@boneskull/typedoc-plugin-mermaid": "^0.2.0", "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/sdk": "1.30.0", "@modelcontextprotocol/server": "2.0.0", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", @@ -33,6 +34,7 @@ "electron-to-chromium": "1.5.267", "esbuild": "^0.25.12", "express": "^5.1.0", + "ext-apps-v1": "npm:@modelcontextprotocol/ext-apps@1.7.5", "husky": "^9.1.7", "nodemon": "^3.1.0", "playwright": "1.57.0", @@ -2756,6 +2758,47 @@ "resolved": "examples/quickstart", "link": true }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/@modelcontextprotocol/server": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz", @@ -4375,6 +4418,41 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -5937,6 +6015,81 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/ext-apps-v1": { + "name": "@modelcontextprotocol/ext-apps", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.5.tgz", + "integrity": "sha512-TjPH2S2y5UEGKhmI6+XGFuqfqOV4ppe1x6DA3txnUaEWkgtA4G5vo14jGKFZmegdkZ1H4QMLyujLvoU1BEdnAg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "examples/*" + ], + "dependencies": { + "@standard-schema/spec": "^1.1.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -6262,7 +6415,6 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.31.tgz", "integrity": "sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -6399,6 +6551,16 @@ "node": ">=12" } }, + "node_modules/ip-address": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6586,6 +6748,20 @@ "node": ">=6" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -7578,6 +7754,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -9716,6 +9902,16 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } } } } diff --git a/package.json b/package.json index da82a66..bcea649 100644 --- a/package.json +++ b/package.json @@ -76,10 +76,12 @@ "update-lock:docker": "rm -rf node_modules package-lock.json examples/*/node_modules && docker run --rm --platform linux/amd64 -v $(pwd):/work -w /work -e HOME=/tmp node:latest npm i --registry=https://registry.npmjs.org/ --ignore-scripts && rm -rf node_modules examples/*/node_modules && npm i --registry=https://registry.npmjs.org/" }, "author": "Olivier Chafik", + "//interop": "The @modelcontextprotocol/sdk 1.x and ext-apps-v1 devDependencies exist only for src/interop-v1.test.ts, which runs the published 1.7.5 package against this build. The library does not use them.", "devDependencies": { "@boneskull/typedoc-plugin-mermaid": "^0.2.0", "@modelcontextprotocol/client": "2.0.0", "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/sdk": "1.30.0", "@modelcontextprotocol/server": "2.0.0", "@playwright/test": "1.57.0", "@types/bun": "^1.3.2", @@ -95,6 +97,7 @@ "electron-to-chromium": "1.5.267", "esbuild": "^0.25.12", "express": "^5.1.0", + "ext-apps-v1": "npm:@modelcontextprotocol/ext-apps@1.7.5", "husky": "^9.1.7", "nodemon": "^3.1.0", "playwright": "1.57.0", diff --git a/src/interop-v1.test.ts b/src/interop-v1.test.ts new file mode 100644 index 0000000..31dbfa1 --- /dev/null +++ b/src/interop-v1.test.ts @@ -0,0 +1,379 @@ +/** + * Cross-version interop against the *published* 1.x package. + * + * `ext-apps-v1` is an npm alias for `@modelcontextprotocol/ext-apps@1.7.5` + * (built on `@modelcontextprotocol/sdk` 1.x). Each pairing wires a real + * `App` to a real `AppBridge` of the other major over an in-memory duplex, + * with the bridge fronting a real MCP `Client`+`McpServer` of its own SDK + * generation, and asserts the observable outcomes. Complements + * `wire-compat.test.ts`, which replays hand-shaped 1.x JSON instead. + */ +import { describe, it, expect, beforeAll, afterAll } from "bun:test"; +import { readFileSync } from "node:fs"; +import { + Client as ClientV2, + InMemoryTransport as InMemoryTransportV2, + ProtocolError, + type Transport, +} from "@modelcontextprotocol/client"; +import { EmptyResultSchema as EmptyResultSchemaV2 } from "@modelcontextprotocol/core"; +import { McpServer as McpServerV2 } from "@modelcontextprotocol/server"; +import { Client as ClientV1 } from "@modelcontextprotocol/sdk/client/index.js"; +import { InMemoryTransport as InMemoryTransportV1 } from "@modelcontextprotocol/sdk/inMemory.js"; +import { McpServer as McpServerV1 } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { Protocol as ProtocolV1 } from "@modelcontextprotocol/sdk/shared/protocol.js"; +import { + EmptyResultSchema as EmptyResultSchemaV1, + McpError, +} from "@modelcontextprotocol/sdk/types.js"; +import { App as AppV1 } from "ext-apps-v1"; +import { AppBridge as AppBridgeV1 } from "ext-apps-v1/app-bridge"; +import { z } from "zod/v4"; + +import { App as AppV2 } from "./app.js"; +import { AppBridge as AppBridgeV2 } from "./app-bridge.js"; + +const flush = () => new Promise((resolve) => setTimeout(resolve, 5)); + +/** In-memory duplex. Structurally a `Transport` for both SDK generations. */ +function createDuplex(): [Transport, Transport] { + const make = () => { + let closed = false; + const t: Transport & { peer?: typeof t } = { + async start() {}, + async send(message) { + // Serialize like a real transport so each side parses plain JSON. + t.peer?.onmessage?.(JSON.parse(JSON.stringify(message))); + }, + async close() { + if (closed) return; + closed = true; + t.onclose?.(); + await t.peer?.close(); + }, + }; + return t; + }; + const [a, b] = [make(), make()]; + a.peer = b; + b.peer = a; + return [a, b]; +} + +/** One SDK generation's MCP pieces, as used by the App side or the host side. */ +const sdkV1 = { + Client: ClientV1, + McpServer: McpServerV1, + InMemoryTransport: InMemoryTransportV1, + EmptyResultSchema: EmptyResultSchemaV1, + error: (code: number, message: string): Error => new McpError(code, message), +}; +const sdkV2 = { + Client: ClientV2, + McpServer: McpServerV2, + InMemoryTransport: InMemoryTransportV2, + EmptyResultSchema: EmptyResultSchemaV2, + error: (code: number, message: string): Error => + new ProtocolError(code, message), +}; + +const pairings = [ + { + name: "2.x App with 1.x AppBridge (sdk 1.x host)", + App: AppV2 as any, + AppBridge: AppBridgeV1 as any, + view: sdkV2, + host: sdkV1, + // sdk 1.x reports a Zod params failure as InternalError. + invalidParamsCode: -32603, + // sdk 1.x forwards a handler-thrown -32002 unchanged. + handlerThrown32002Code: -32002, + // McpServer 1.x answers an unknown tool with an isError result. + unknownToolRejects: false, + }, + { + name: "1.x App with 2.x AppBridge (sdk 2.x host)", + App: AppV1 as any, + AppBridge: AppBridgeV2 as any, + view: sdkV1, + host: sdkV2, + invalidParamsCode: -32602, + // SDK 2.x never emits -32002 on the wire (see wire-compat.test.ts). + handlerThrown32002Code: -32602, + // McpServer 2.x answers an unknown tool with a JSON-RPC error. + unknownToolRejects: true, + }, +]; + +const hostInfo = { name: "TestHost", version: "9.9.9" }; +const appInfo = { name: "TestView", version: "1.2.3" }; +const hostCapabilities = { + openLinks: {}, + serverTools: {}, + serverResources: {}, + logging: {}, + updateModelContext: {}, +}; +const hostContext = { theme: "dark", displayMode: "inline", locale: "en-US" }; + +it("pits 2.x against the published 1.x package, not against itself", () => { + const v1PackageJson = JSON.parse( + readFileSync( + new URL("../../package.json", import.meta.resolve("ext-apps-v1")), + "utf8", + ), + ); + expect(v1PackageJson).toMatchObject({ + name: "@modelcontextprotocol/ext-apps", + version: "1.7.5", + }); + // 1.x classes extend the sdk-1 Protocol; 2.x classes do not. + expect(AppV1.prototype).toBeInstanceOf(ProtocolV1); + expect(AppBridgeV1.prototype).toBeInstanceOf(ProtocolV1); + expect(AppV2.prototype).not.toBeInstanceOf(ProtocolV1); + expect(AppBridgeV2.prototype).not.toBeInstanceOf(ProtocolV1); +}); + +// The tests in each pairing share one connected App/AppBridge fixture and run +// in file order (bun does not randomize); later tests assume earlier ones +// left the pair connected. +describe.each(pairings)("$name", (pairing) => { + const { view, host } = pairing; + let server: any, client: any, bridge: any, app: any; + const events: string[] = []; + const record = (name: string) => (params?: unknown) => { + events.push(`${name}:${JSON.stringify(params ?? null)}`); + }; + const drain = () => events.splice(0); + // Captured inside oninitialized: the bridge must have processed + // ui/initialize before ui/notifications/initialized arrives. + let appVersionAtInitialized: unknown = "not-fired"; + let hostAbortReason: unknown; + + beforeAll(async () => { + server = new host.McpServer({ name: "TestServer", version: "1.0.0" }); + server.registerTool( + "echo", + { inputSchema: z.object({ a: z.number() }) }, + async ({ a }: { a: number }) => ({ + content: [{ type: "text", text: `echo ${a}` }], + }), + ); + server.registerTool("boom", { inputSchema: z.object({}) }, async () => { + throw new Error("tool exploded"); + }); + server.registerResource( + "res", + "test://res", + { mimeType: "text/plain" }, + async (uri: URL) => ({ + contents: [{ uri: uri.href, mimeType: "text/plain", text: "hello" }], + }), + ); + const [clientTransport, serverTransport] = + host.InMemoryTransport.createLinkedPair(); + await server.connect(serverTransport); + client = new host.Client({ name: "HostOuterClient", version: "1.0.0" }); + await client.connect(clientTransport); + + bridge = new pairing.AppBridge(client, hostInfo, hostCapabilities, { + hostContext, + }); + app = new pairing.App( + appInfo, + { tools: { listChanged: true } }, + { + autoResize: false, + }, + ); + bridge.oninitialized = () => { + appVersionAtInitialized = bridge.getAppVersion(); + }; + for (const name of ["onsizechange", "onloggingmessage"]) { + bridge[name] = record(`bridge.${name}`); + } + for (const name of ["onmessage", "onupdatemodelcontext"]) { + bridge[name] = async (params: unknown) => { + record(`bridge.${name}`)(params); + return {}; + }; + } + bridge.onopenlink = (params: { url: string }, extra: any) => { + record("bridge.onopenlink")(params); + if (params.url !== "https://slow") return {}; + // Hang until the view cancels; sdk 1.x passes `extra.signal`, 2.x `extra.mcpReq.signal`. + const signal: AbortSignal = extra.signal ?? extra.mcpReq.signal; + return new Promise((_resolve, reject) => { + signal.addEventListener("abort", () => { + hostAbortReason = signal.reason; + reject(new Error("aborted")); + }); + }); + }; + for (const name of [ + "ontoolinput", + "ontoolresult", + "ontoolcancelled", + "onhostcontextchanged", + ]) { + app[name] = record(`app.${name}`); + } + app.onteardown = async (params: unknown) => { + record("app.onteardown")(params); + return {}; + }; + }); + + afterAll(async () => { + await app?.close().catch(() => {}); + await bridge?.close().catch(() => {}); + await client?.close().catch(() => {}); + await server?.close().catch(() => {}); + }); + + it("completes ui/initialize and delivers initialized in order", async () => { + const [appTransport, bridgeTransport] = createDuplex(); + // Tap the raw ui/initialize result so an additive key on the 2.x side + // (which the getters below would not surface) still fails here. + let initializeResult: unknown; + const send = bridgeTransport.send.bind(bridgeTransport); + bridgeTransport.send = async (message, options) => { + if ("result" in message && "id" in message && message.id === 0) { + initializeResult = message.result; + } + await send(message, options); + }; + await bridge.connect(bridgeTransport); + await app.connect(appTransport); + await flush(); + + expect(app.getHostContext()).toEqual(hostContext); + expect(app.getHostCapabilities()).toEqual(hostCapabilities); + expect(app.getHostVersion()).toEqual(hostInfo); + expect(bridge.getAppCapabilities()).toEqual({ + tools: { listChanged: true }, + }); + expect(appVersionAtInitialized).toEqual(appInfo); + expect(initializeResult).toEqual({ + protocolVersion: "2026-01-26", + hostCapabilities, + hostInfo, + hostContext, + }); + }); + + it("delivers host-to-view notifications", async () => { + bridge.sendToolInput({ arguments: { location: "NYC" } }); + bridge.sendToolResult({ content: [{ type: "text", text: "sunny" }] }); + bridge.sendToolCancelled({ reason: "user" }); + bridge.setHostContext({ theme: "light" }); + await flush(); + + expect(drain()).toEqual([ + 'app.ontoolinput:{"arguments":{"location":"NYC"}}', + 'app.ontoolresult:{"content":[{"type":"text","text":"sunny"}]}', + 'app.ontoolcancelled:{"reason":"user"}', + 'app.onhostcontextchanged:{"theme":"light"}', + ]); + expect(app.getHostContext()).toEqual({ ...hostContext, theme: "light" }); + }); + + it("round-trips view-to-host requests and notifications", async () => { + const message = { role: "user", content: [{ type: "text", text: "hi" }] }; + expect(await app.sendMessage(message)).toEqual({}); + expect(await app.openLink({ url: "https://example.com" })).toEqual({}); + expect(await app.requestDisplayMode({ mode: "fullscreen" })).toEqual({ + mode: "inline", + }); + bridge.onrequestdisplaymode = async (params: { mode: string }) => params; + expect(await app.requestDisplayMode({ mode: "fullscreen" })).toEqual({ + mode: "fullscreen", + }); + const context = { content: [{ type: "text", text: "ctx" }] }; + expect(await app.updateModelContext(context)).toEqual({}); + await app.sendLog({ level: "info", data: { m: "hello" } }); + await app.sendSizeChanged({ width: 320, height: 240 }); + await flush(); + + expect(drain()).toEqual([ + `bridge.onmessage:${JSON.stringify(message)}`, + 'bridge.onopenlink:{"url":"https://example.com"}', + `bridge.onupdatemodelcontext:${JSON.stringify(context)}`, + 'bridge.onloggingmessage:{"level":"info","data":{"m":"hello"}}', + 'bridge.onsizechange:{"width":320,"height":240}', + ]); + }); + + it("proxies tools/call and resources/read to the real MCP server", async () => { + expect( + await app.callServerTool({ name: "echo", arguments: { a: 1 } }), + ).toEqual({ content: [{ type: "text", text: "echo 1" }] }); + expect( + await app.callServerTool({ name: "boom", arguments: {} }), + ).toMatchObject({ + isError: true, + content: [{ type: "text", text: "tool exploded" }], + }); + const unknown = app.callServerTool({ name: "nope", arguments: {} }); + if (pairing.unknownToolRejects) { + await expect(unknown).rejects.toMatchObject({ code: -32602 }); + } else { + expect(await unknown).toMatchObject({ isError: true }); + } + expect(await app.readServerResource({ uri: "test://res" })).toEqual({ + contents: [{ uri: "test://res", mimeType: "text/plain", text: "hello" }], + }); + }); + + it("propagates JSON-RPC errors with the expected codes", async () => { + const raw = (method: string, params: unknown) => + app.request({ method, params }, view.EmptyResultSchema); + await expect(raw("ui/does-not-exist", {})).rejects.toMatchObject({ + code: -32601, + }); + await expect(raw("ui/open-link", { url: 42 })).rejects.toMatchObject({ + code: pairing.invalidParamsCode, + }); + bridge.onreadresource = async () => { + throw host.error(-32002, "Resource not found (host-thrown -32002)"); + }; + const notFound = app.readServerResource({ uri: "test://missing" }); + await expect(notFound).rejects.toMatchObject({ + code: pairing.handlerThrown32002Code, + message: expect.stringContaining( + "Resource not found (host-thrown -32002)", + ), + }); + }); + + it("cancels an in-flight request via AbortSignal on both ends", async () => { + const controller = new AbortController(); + const pending = app.openLink( + { url: "https://slow" }, + { signal: controller.signal }, + ); + await flush(); + controller.abort("user-cancelled"); + await expect(pending).rejects.toMatchObject({ + message: expect.stringContaining("user-cancelled"), + }); + await flush(); + expect(String(hostAbortReason)).toContain("user-cancelled"); + drain(); + }); + + it("tears down, then closes both ends", async () => { + expect(await bridge.teardownResource({})).toEqual({}); + expect(drain()).toEqual(["app.onteardown:{}"]); + + const closed: string[] = []; + app.onclose = () => closed.push("app"); + bridge.onclose = () => closed.push("bridge"); + await app.close(); + await flush(); + expect(closed.sort()).toEqual(["app", "bridge"]); + await expect(app.openLink({ url: "https://after-close" })).rejects.toThrow( + "Not connected", + ); + }); +}); From 853c9f749c2c04e9b8391919905935dee411300b Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 18:01:18 +0100 Subject: [PATCH 42/45] Migration guide fixes and editor-visible deprecation for the 1.x handler form Guide: name @modelcontextprotocol/express in the server-author row, say the two SDKs share no types rather than "do not interoperate" (they speak the same wire protocol), note zod 4.0/4.1 lack ~standard.jsonSchema, list the full 1.x-shaped extra, cover tool callbacks in the handler-context bullet, document that the double-registration guard now covers the base Protocol's notifications/progress and notifications/cancelled handlers, mention ResourceNotFoundError, add the Express row to the import map, and add a "What stays the same" section. The @deprecated tag on the legacy setRequestHandler / setNotificationHandler overload sat on the parameter inside an intersection type, which TypeScript does not report. The overload now comes from LegacyRequestHandlerSetter / LegacyNotificationHandlerSetter, named types with a documented call signature, so editors flag the 1.x call and not the 2.x forms. README links to the quickstart use the lowercase slug typedoc emits. --- README.md | 6 ++--- docs/migrate-to-2.md | 55 ++++++++++++++++++++++++++++++------------ src/app-bridge.ts | 23 ++++++------------ src/app.ts | 25 ++++++++----------- src/legacy-handlers.ts | 27 +++++++++++++++++++++ 5 files changed, 87 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index acfc661..ab4004d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

Why · - Quickstart + Quickstart · API Docs · @@ -151,7 +151,7 @@ in a 1.x host and a 2.x host renders 1.x Views. See the when upgrading from 1.x. **New here?** Start with the -[Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html) +[Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html) to build your first MCP App. ## Using the SDK @@ -272,7 +272,7 @@ point your client at a local build: ## Resources -- [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html) +- [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html) - [API Documentation](https://apps.extensions.modelcontextprotocol.io/api/) - [Specification (2026-01-26)](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx) ([Draft](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)) diff --git a/docs/migrate-to-2.md b/docs/migrate-to-2.md index 515c363..f9341d0 100644 --- a/docs/migrate-to-2.md +++ b/docs/migrate-to-2.md @@ -17,14 +17,22 @@ The `ui/*` messages exchanged over the iframe channel are byte-identical to 1.x. A 2.x View runs in a 1.x host and a 2.x host renders 1.x Views. The only host-side deltas are in error responses (see below). +## What stays the same + +`App` / `AppBridge` constructors, the `on*` setters, `addEventListener`, +`callServerTool`, `readServerResource`, `registerAppResource`, the React hooks +and the `_meta.ui.resourceUri` handling are unchanged. Published `.d.ts` files +now resolve under `moduleResolution: NodeNext` / `Node16` without workarounds +(#705). + ## Peer dependencies by role -| Role | Install | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -| View author | `@modelcontextprotocol/ext-apps`, `@modelcontextprotocol/client@^2.0.0`, `zod@^4.2.0` (+ `react`/`react-dom` for `./react`) | -| Host author | same as View author | -| MCP server author | View author packages + `@modelcontextprotocol/server@^2.0.0`; `@modelcontextprotocol/node` and `express` for HTTP transports | -| CDN / `*-with-deps` | nothing extra: `./app-with-deps` and `./react-with-deps` bundle client, core and zod (about 25% larger than the 1.x bundles) | +| Role | Install | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| View author | `@modelcontextprotocol/ext-apps`, `@modelcontextprotocol/client@^2.0.0`, `zod@^4.2.0` (+ `react`/`react-dom` for `./react`) | +| Host author | same as View author | +| MCP server author | View author packages + `@modelcontextprotocol/server@^2.0.0`; for HTTP transports also `@modelcontextprotocol/node@^2.0.0` (`NodeStreamableHTTPServerTransport`) and `@modelcontextprotocol/express@^2.0.0` (`createMcpExpressApp`, which peers on `express`) | +| CDN / `*-with-deps` | nothing extra: `./app-with-deps` and `./react-with-deps` bundle client, core and zod (about 25% larger than the 1.x bundles) | `@modelcontextprotocol/client` is a required peer (`App` and `AppBridge` extend its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `client` already depends on, so npm installs it without you listing it; @@ -35,18 +43,23 @@ its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `cli - **Peer packages.** `@modelcontextprotocol/sdk@^1` is replaced by the split 2.x packages above, all at `^2.0.0`. Remove the 1.x package from your - project; the two SDKs do not interoperate. + project: the two SDKs share no classes or types (a 1.x `McpError` or + `Client` cannot be passed to 2.x code), even though they speak the same + wire protocol. - **zod 3 dropped.** The peer range is `zod@^4.2.0`. Tool schemas must - implement Standard JSON Schema (`~standard.jsonSchema`): zod v4, ArkType, - Valibot. Raw zod shapes (`{ q: z.string() }`) still work with + implement Standard JSON Schema (`~standard.jsonSchema`): zod 4.2+, ArkType, + Valibot. zod 4.0 and 4.1 do not expose `~standard.jsonSchema` and are + rejected too. Raw zod shapes (`{ q: z.string() }`) still work with `registerAppTool` but are deprecated; wrap them with `z.object({...})`. - **`App` / `AppBridge` extend `Protocol` from `@modelcontextprotocol/client`.** `ProtocolWithEvents` is gone; use the SDK's `Protocol`. The `AppRequest`, `AppNotification` and `AppResult` unions are kept as deprecated type aliases (nothing in 2.x consumes them) and will be removed in 3.0. -- **Handler context.** Custom handlers receive the SDK 2.x context: - `extra.signal` → `extra.mcpReq.signal`, `extra.requestId` → - `extra.mcpReq.id`. +- **Handler context.** Custom request handlers, `registerAppTool` callbacks + (server) and `app.registerTool` callbacks (View) receive the SDK 2.x + context: `extra.signal` → `extra.mcpReq.signal`, `extra.requestId` → + `extra.mcpReq.id`; `extra.sessionId` is unchanged and `extra.authInfo` + moved to `extra.http?.authInfo`. - **`setRequestHandler` / `setNotificationHandler` take method names.** `app.setRequestHandler(SomeRequestSchema, handler)` becomes `app.setRequestHandler("some/method", { params: SomeParamsSchema }, (params, ctx) => …)` @@ -55,14 +68,23 @@ its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `cli method names. The 1.x `(Schema, handler)` form still works on `App` and `AppBridge` as a deprecated overload: it logs a one-time warning, hands the handler the whole `{ method, params }` message as before, and gives request - handlers a 1.x-shaped `extra` (`signal`, `requestId`, `sessionId`, `_meta`). - It will be removed in 3.0. + handlers a 1.x-shaped `extra` (`signal`, `requestId`, `sessionId`, `_meta`, + `sendRequest`, `sendNotification`, `authInfo`; exported as + `LegacyRequestHandlerExtra`). It will be removed in 3.0. +- **Double-registration guard covers the SDK's own handlers.** Registering a + handler for `notifications/progress` or `notifications/cancelled` (which + the base `Protocol` already owns) now throws `already registered`, as + `ping` and the `on*`-owned methods did in 1.x. Use `addEventListener` or + the SDK's progress callbacks instead of replacing these. - **Errors.** Remote JSON-RPC errors are `ProtocolError` (numeric `code`); local conditions are `SdkError` with a string `code`: request timeout → `"REQUEST_TIMEOUT"`, connection closed → `"CONNECTION_CLOSED"`. Cancelling a request with an `AbortSignal` also rejects with `"REQUEST_TIMEOUT"` (the message is the abort reason). Messages no longer carry the `MCP error N:` prefix. + A resource-not-found reply that carries `data.uri` arrives as + `ResourceNotFoundError` (a `ProtocolError` subclass, `code` `-32602`, with a + `.uri` getter). ## Host-side wire deltas @@ -98,8 +120,9 @@ The published `./schema.json` export is regenerated from the 2.x core schemas: 2. Replace `sdk/...` imports with the split packages (`sdk/server/mcp.js` → `@modelcontextprotocol/server`, `sdk/server/streamableHttp.js` → `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node`, - `sdk/server/stdio.js` → `@modelcontextprotocol/server/stdio`, `sdk/types.js` - → `@modelcontextprotocol/client` or `server` for the types, + `sdk/server/stdio.js` → `@modelcontextprotocol/server/stdio`, hand-written + Express routing → `createMcpExpressApp` from `@modelcontextprotocol/express`, + `sdk/types.js` → `@modelcontextprotocol/client` or `server` for the types, `@modelcontextprotocol/core` for the zod schemas). 3. Wrap raw zod shapes with `z.object({...})`. 4. Update custom handlers to the `extra.mcpReq.*` context and method-keyed diff --git a/src/app-bridge.ts b/src/app-bridge.ts index c61dfa4..1ac4db4 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -37,9 +37,8 @@ import { import { EventDispatcher, MethodRegistry } from "./events.js"; import { toModernArgs, - type LegacyMethodSchema, - type LegacyNotificationHandler, - type LegacyRequestHandler, + type LegacyNotificationHandlerSetter, + type LegacyRequestHandlerSetter, } from "./legacy-handlers.js"; import type { ZodLiteral, ZodObject, ZodType } from "zod/v4"; @@ -330,12 +329,9 @@ export class AppBridge extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setRequestHandler: Protocol["setRequestHandler"] & - (( - /** @deprecated Pass the method name and `{ params }` instead. */ - schema: S, - handler: LegacyRequestHandler, - ) => void) = (...args: unknown[]) => { + override setRequestHandler: LegacyRequestHandlerSetter< + Protocol["setRequestHandler"] + > = (...args: unknown[]) => { const [method, ...rest] = toModernArgs("request", args) ?? args; this._methods.claim(method as string, "setRequestHandler"); (super.setRequestHandler as unknown as UntypedHandlerSetter).call( @@ -352,12 +348,9 @@ export class AppBridge extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setNotificationHandler: Protocol["setNotificationHandler"] & - (( - /** @deprecated Pass the method name and `{ params }` instead. */ - schema: S, - handler: LegacyNotificationHandler, - ) => void) = (...args: unknown[]) => { + override setNotificationHandler: LegacyNotificationHandlerSetter< + Protocol["setNotificationHandler"] + > = (...args: unknown[]) => { const [method, ...rest] = toModernArgs("notification", args) ?? args; this._methods.claim(method as string, "setNotificationHandler"); (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( diff --git a/src/app.ts b/src/app.ts index 0ab51ac..89c6c35 100644 --- a/src/app.ts +++ b/src/app.ts @@ -26,15 +26,16 @@ export { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "./constants.js"; import { EventDispatcher, MethodRegistry } from "./events.js"; import { toModernArgs, - type LegacyMethodSchema, - type LegacyNotificationHandler, - type LegacyRequestHandler, + type LegacyNotificationHandlerSetter, + type LegacyRequestHandlerSetter, } from "./legacy-handlers.js"; export type { LegacyMethodSchema, LegacyNotificationHandler, + LegacyNotificationHandlerSetter, LegacyRequestHandler, LegacyRequestHandlerExtra, + LegacyRequestHandlerSetter, } from "./legacy-handlers.js"; export { EventDispatcher } from "./events.js"; @@ -328,12 +329,9 @@ export class App extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setRequestHandler: Protocol["setRequestHandler"] & - (( - /** @deprecated Pass the method name and `{ params }` instead. */ - schema: S, - handler: LegacyRequestHandler, - ) => void) = (...args: unknown[]) => { + override setRequestHandler: LegacyRequestHandlerSetter< + Protocol["setRequestHandler"] + > = (...args: unknown[]) => { const [method, ...rest] = toModernArgs("request", args) ?? args; this._methods.claim(method as string, "setRequestHandler"); (super.setRequestHandler as unknown as UntypedHandlerSetter).call( @@ -350,12 +348,9 @@ export class App extends Protocol { * * @throws {Error} if a handler for this method is already registered. */ - override setNotificationHandler: Protocol["setNotificationHandler"] & - (( - /** @deprecated Pass the method name and `{ params }` instead. */ - schema: S, - handler: LegacyNotificationHandler, - ) => void) = (...args: unknown[]) => { + override setNotificationHandler: LegacyNotificationHandlerSetter< + Protocol["setNotificationHandler"] + > = (...args: unknown[]) => { const [method, ...rest] = toModernArgs("notification", args) ?? args; this._methods.claim(method as string, "setNotificationHandler"); (super.setNotificationHandler as unknown as UntypedHandlerSetter).call( diff --git a/src/legacy-handlers.ts b/src/legacy-handlers.ts index b4d1157..237b8eb 100644 --- a/src/legacy-handlers.ts +++ b/src/legacy-handlers.ts @@ -36,6 +36,33 @@ export type LegacyNotificationHandler = ( notification: S["_output"], ) => void | Promise; +/** + * `setRequestHandler` with the 1.x `(Schema, handler)` form kept as a + * deprecated overload next to the 2.x forms in `Modern`. + */ +export type LegacyRequestHandlerSetter = Modern & { + /** + * @deprecated 1.x form. Pass the method name and `{ params }` instead; + * removed in 3.0. + */ + ( + schema: S, + handler: LegacyRequestHandler, + ): void; +}; + +/** `setNotificationHandler` counterpart of {@link LegacyRequestHandlerSetter}. */ +export type LegacyNotificationHandlerSetter = Modern & { + /** + * @deprecated 1.x form. Pass the method name and `{ params }` instead; + * removed in 3.0. + */ + ( + schema: S, + handler: LegacyNotificationHandler, + ): void; +}; + /** * Arguments for the 2.x three-argument `setRequestHandler` / * `setNotificationHandler` form. From a0dcf168432d52ad1e71cd3a37958ed0c03849b3 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 18:38:58 +0100 Subject: [PATCH 43/45] CI: build the WSL job on ext4, skip the unused Chromium download, list tsconfig types The Windows WSL job is the critical path of every CI run (589 to 752 s). Its checkout sits on the Windows drive, which WSL reaches over 9P, and writing node_modules there is what makes npm ci take about three minutes where the Linux job takes 23 s. The step now copies the tree onto the distro's ext4 filesystem before installing and building, as Microsoft's WSL guidance recommends for I/O-heavy work. The e2e job downloaded the full Chromium build (about 165 MB) on every run, but a headless run with no channel launches chromium-headless-shell and never opens it. `--only-shell` skips that download. Every tsconfig now lists the @types packages it uses (node, bun, vite/client, dom-speech-recognition) instead of relying on TypeScript auto-including everything under node_modules/@types. On TypeScript 5.9 this is a no-op: identical diagnostics across all 48 projects and byte-identical emitted .d.ts files. It is a prerequisite for TypeScript 7, which drops the auto-inclusion; with these lists the Go compiler passes all 48 projects, so that upgrade becomes a version bump once TypeDoc supports it. Two cache-based approaches were evaluated and rejected: an npm cache for the WSL job (npm ci is slow there because of 9P, not downloads, and a prefix-restored cache grows without bound) and a Playwright browser cache (Playwright's docs advise against it; the download is under 10 s). --- .github/workflows/ci.yml | 9 ++++++++- examples/basic-host/tsconfig.json | 1 + examples/basic-server-preact/tsconfig.json | 1 + examples/basic-server-preact/tsconfig.server.json | 1 + examples/basic-server-react/tsconfig.json | 1 + examples/basic-server-react/tsconfig.server.json | 1 + examples/basic-server-solid/tsconfig.json | 1 + examples/basic-server-solid/tsconfig.server.json | 1 + examples/basic-server-svelte/tsconfig.json | 1 + examples/basic-server-svelte/tsconfig.server.json | 1 + examples/basic-server-vanillajs/tsconfig.json | 1 + examples/basic-server-vanillajs/tsconfig.server.json | 1 + examples/basic-server-vue/tsconfig.json | 1 + examples/basic-server-vue/tsconfig.server.json | 1 + examples/budget-allocator-server/tsconfig.json | 1 + examples/budget-allocator-server/tsconfig.server.json | 1 + examples/cohort-heatmap-server/tsconfig.json | 1 + examples/cohort-heatmap-server/tsconfig.server.json | 1 + examples/customer-segmentation-server/tsconfig.json | 1 + .../customer-segmentation-server/tsconfig.server.json | 1 + examples/debug-server/tsconfig.json | 1 + examples/debug-server/tsconfig.server.json | 1 + examples/integration-server/tsconfig.json | 1 + examples/integration-server/tsconfig.server.json | 1 + examples/lazy-auth-server/tsconfig.json | 1 + examples/lazy-auth-server/tsconfig.server.json | 1 + examples/map-server/tsconfig.json | 1 + examples/map-server/tsconfig.server.json | 1 + examples/pdf-server/tsconfig.json | 1 + examples/pdf-server/tsconfig.server.json | 1 + examples/quickstart/tsconfig.json | 1 + examples/quickstart/tsconfig.server.json | 1 + examples/scenario-modeler-server/tsconfig.json | 1 + examples/scenario-modeler-server/tsconfig.server.json | 1 + examples/shadertoy-server/tsconfig.json | 1 + examples/shadertoy-server/tsconfig.server.json | 1 + examples/sheet-music-server/tsconfig.json | 1 + examples/sheet-music-server/tsconfig.server.json | 1 + examples/system-monitor-server/tsconfig.json | 1 + examples/system-monitor-server/tsconfig.server.json | 1 + examples/threejs-server/tsconfig.json | 1 + examples/threejs-server/tsconfig.server.json | 1 + examples/transcript-server/tsconfig.json | 1 + examples/transcript-server/tsconfig.server.json | 1 + examples/video-resource-server/tsconfig.json | 1 + examples/video-resource-server/tsconfig.server.json | 1 + examples/wiki-explorer-server/tsconfig.json | 1 + examples/wiki-explorer-server/tsconfig.server.json | 1 + tsconfig.json | 1 + 49 files changed, 56 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 756f259..6848e4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,8 +147,10 @@ jobs: - run: npm ci + # Headless runs without a channel use chromium-headless-shell, so the + # full Chromium build (about 165 MB) would only be downloaded, never run. - name: Install Playwright browsers - run: npx playwright install --with-deps chromium + run: npx playwright install --with-deps --only-shell chromium - name: Run E2E tests run: npx playwright test --reporter=list @@ -180,9 +182,14 @@ jobs: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs + # The checkout lives on the Windows drive, which WSL reaches over 9P; + # writing node_modules there is what makes this job slow (npm ci alone + # took about 3 minutes). Copy the tree onto the distro's ext4 filesystem + # first, as Microsoft's WSL guidance recommends for anything I/O heavy. - name: Build and test in WSL shell: wsl-bash {0} run: | + cp -r "$(pwd)" ~/ext-apps && cd ~/ext-apps npm ci npm run build npm run examples:build diff --git a/examples/basic-host/tsconfig.json b/examples/basic-host/tsconfig.json index 9d7c3bc..3c143ca 100644 --- a/examples/basic-host/tsconfig.json +++ b/examples/basic-host/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-preact/tsconfig.json b/examples/basic-server-preact/tsconfig.json index 32182d9..9bf9389 100644 --- a/examples/basic-server-preact/tsconfig.json +++ b/examples/basic-server-preact/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-preact/tsconfig.server.json b/examples/basic-server-preact/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/basic-server-preact/tsconfig.server.json +++ b/examples/basic-server-preact/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/basic-server-react/tsconfig.json b/examples/basic-server-react/tsconfig.json index fc3c210..3ecbdad 100644 --- a/examples/basic-server-react/tsconfig.json +++ b/examples/basic-server-react/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-react/tsconfig.server.json b/examples/basic-server-react/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/basic-server-react/tsconfig.server.json +++ b/examples/basic-server-react/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/basic-server-solid/tsconfig.json b/examples/basic-server-solid/tsconfig.json index 84f0efe..bb13a01 100644 --- a/examples/basic-server-solid/tsconfig.json +++ b/examples/basic-server-solid/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-solid/tsconfig.server.json b/examples/basic-server-solid/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/basic-server-solid/tsconfig.server.json +++ b/examples/basic-server-solid/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/basic-server-svelte/tsconfig.json b/examples/basic-server-svelte/tsconfig.json index 5edb66a..4fd3cd7 100644 --- a/examples/basic-server-svelte/tsconfig.json +++ b/examples/basic-server-svelte/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-svelte/tsconfig.server.json b/examples/basic-server-svelte/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/basic-server-svelte/tsconfig.server.json +++ b/examples/basic-server-svelte/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/basic-server-vanillajs/tsconfig.json b/examples/basic-server-vanillajs/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/basic-server-vanillajs/tsconfig.json +++ b/examples/basic-server-vanillajs/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-vanillajs/tsconfig.server.json b/examples/basic-server-vanillajs/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/basic-server-vanillajs/tsconfig.server.json +++ b/examples/basic-server-vanillajs/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/basic-server-vue/tsconfig.json b/examples/basic-server-vue/tsconfig.json index 5edb66a..4fd3cd7 100644 --- a/examples/basic-server-vue/tsconfig.json +++ b/examples/basic-server-vue/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/basic-server-vue/tsconfig.server.json b/examples/basic-server-vue/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/basic-server-vue/tsconfig.server.json +++ b/examples/basic-server-vue/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/budget-allocator-server/tsconfig.json b/examples/budget-allocator-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/budget-allocator-server/tsconfig.json +++ b/examples/budget-allocator-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/budget-allocator-server/tsconfig.server.json b/examples/budget-allocator-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/budget-allocator-server/tsconfig.server.json +++ b/examples/budget-allocator-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/cohort-heatmap-server/tsconfig.json b/examples/cohort-heatmap-server/tsconfig.json index fc3c210..3ecbdad 100644 --- a/examples/cohort-heatmap-server/tsconfig.json +++ b/examples/cohort-heatmap-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/cohort-heatmap-server/tsconfig.server.json b/examples/cohort-heatmap-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/cohort-heatmap-server/tsconfig.server.json +++ b/examples/cohort-heatmap-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/customer-segmentation-server/tsconfig.json b/examples/customer-segmentation-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/customer-segmentation-server/tsconfig.json +++ b/examples/customer-segmentation-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/customer-segmentation-server/tsconfig.server.json b/examples/customer-segmentation-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/customer-segmentation-server/tsconfig.server.json +++ b/examples/customer-segmentation-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/debug-server/tsconfig.json b/examples/debug-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/debug-server/tsconfig.json +++ b/examples/debug-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/debug-server/tsconfig.server.json b/examples/debug-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/debug-server/tsconfig.server.json +++ b/examples/debug-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/integration-server/tsconfig.json b/examples/integration-server/tsconfig.json index fc3c210..3ecbdad 100644 --- a/examples/integration-server/tsconfig.json +++ b/examples/integration-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/integration-server/tsconfig.server.json b/examples/integration-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/integration-server/tsconfig.server.json +++ b/examples/integration-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/lazy-auth-server/tsconfig.json b/examples/lazy-auth-server/tsconfig.json index 6c553b5..d6a42ce 100644 --- a/examples/lazy-auth-server/tsconfig.json +++ b/examples/lazy-auth-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/lazy-auth-server/tsconfig.server.json b/examples/lazy-auth-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/lazy-auth-server/tsconfig.server.json +++ b/examples/lazy-auth-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/map-server/tsconfig.json b/examples/map-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/map-server/tsconfig.json +++ b/examples/map-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/map-server/tsconfig.server.json b/examples/map-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/map-server/tsconfig.server.json +++ b/examples/map-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/pdf-server/tsconfig.json b/examples/pdf-server/tsconfig.json index 0501827..2fe7e62 100644 --- a/examples/pdf-server/tsconfig.json +++ b/examples/pdf-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client", "bun"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/pdf-server/tsconfig.server.json b/examples/pdf-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/pdf-server/tsconfig.server.json +++ b/examples/pdf-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/quickstart/tsconfig.json b/examples/quickstart/tsconfig.json index 6c553b5..d6a42ce 100644 --- a/examples/quickstart/tsconfig.json +++ b/examples/quickstart/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/quickstart/tsconfig.server.json b/examples/quickstart/tsconfig.server.json index 7e65f5f..72552e2 100644 --- a/examples/quickstart/tsconfig.server.json +++ b/examples/quickstart/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/scenario-modeler-server/tsconfig.json b/examples/scenario-modeler-server/tsconfig.json index fc3c210..3ecbdad 100644 --- a/examples/scenario-modeler-server/tsconfig.json +++ b/examples/scenario-modeler-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/scenario-modeler-server/tsconfig.server.json b/examples/scenario-modeler-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/scenario-modeler-server/tsconfig.server.json +++ b/examples/scenario-modeler-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/shadertoy-server/tsconfig.json b/examples/shadertoy-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/shadertoy-server/tsconfig.json +++ b/examples/shadertoy-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/shadertoy-server/tsconfig.server.json b/examples/shadertoy-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/shadertoy-server/tsconfig.server.json +++ b/examples/shadertoy-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/sheet-music-server/tsconfig.json b/examples/sheet-music-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/sheet-music-server/tsconfig.json +++ b/examples/sheet-music-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/sheet-music-server/tsconfig.server.json b/examples/sheet-music-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/sheet-music-server/tsconfig.server.json +++ b/examples/sheet-music-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/system-monitor-server/tsconfig.json b/examples/system-monitor-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/system-monitor-server/tsconfig.json +++ b/examples/system-monitor-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/system-monitor-server/tsconfig.server.json b/examples/system-monitor-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/system-monitor-server/tsconfig.server.json +++ b/examples/system-monitor-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/threejs-server/tsconfig.json b/examples/threejs-server/tsconfig.json index fc3c210..3ecbdad 100644 --- a/examples/threejs-server/tsconfig.json +++ b/examples/threejs-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/threejs-server/tsconfig.server.json b/examples/threejs-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/threejs-server/tsconfig.server.json +++ b/examples/threejs-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/transcript-server/tsconfig.json b/examples/transcript-server/tsconfig.json index 535267b..00e4801 100644 --- a/examples/transcript-server/tsconfig.json +++ b/examples/transcript-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client", "dom-speech-recognition"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/transcript-server/tsconfig.server.json b/examples/transcript-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/transcript-server/tsconfig.server.json +++ b/examples/transcript-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/video-resource-server/tsconfig.json b/examples/video-resource-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/video-resource-server/tsconfig.json +++ b/examples/video-resource-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/video-resource-server/tsconfig.server.json b/examples/video-resource-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/video-resource-server/tsconfig.server.json +++ b/examples/video-resource-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/examples/wiki-explorer-server/tsconfig.json b/examples/wiki-explorer-server/tsconfig.json index 535267b..b028ff5 100644 --- a/examples/wiki-explorer-server/tsconfig.json +++ b/examples/wiki-explorer-server/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", diff --git a/examples/wiki-explorer-server/tsconfig.server.json b/examples/wiki-explorer-server/tsconfig.server.json index 05ddd8e..27eb28f 100644 --- a/examples/wiki-explorer-server/tsconfig.server.json +++ b/examples/wiki-explorer-server/tsconfig.server.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", diff --git a/tsconfig.json b/tsconfig.json index 08ff110..934b950 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "bun"], "target": "ES2020", "module": "ESNext", "lib": ["ES2020", "DOM"], From ae4f7e0ad0607ed46862a26af664a05f94b0246a Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 18:39:50 +0100 Subject: [PATCH 44/45] Sync the quickstart tsconfig snippets --- docs/quickstart.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/quickstart.md b/docs/quickstart.md index 2039dc1..a021912 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -59,6 +59,7 @@ npm pkg set scripts.start='concurrently --raw "cross-env NODE_ENV=development IN ```json source="../examples/quickstart/tsconfig.json" { "compilerOptions": { + "types": ["node", "vite/client"], "target": "ESNext", "lib": ["ESNext", "DOM", "DOM.Iterable"], "module": "ESNext", @@ -87,6 +88,7 @@ npm pkg set scripts.start='concurrently --raw "cross-env NODE_ENV=development IN ```json source="../examples/quickstart/tsconfig.server.json" { "compilerOptions": { + "types": ["node"], "target": "ES2022", "lib": ["ES2022"], "module": "NodeNext", From 2be172defbadb4dc2a92b2cb447b534aebb56d41 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 8 Sep 2026 19:37:47 +0100 Subject: [PATCH 45/45] Use the canonical git+https form for repository.url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm normalizes the bare https URL to git+https://….git at publish time and warns about it on every package. Storing the canonical form removes the warning and matches what npm's trusted-publisher check compares against the repository. --- examples/basic-server-preact/package.json | 2 +- examples/basic-server-react/package.json | 2 +- examples/basic-server-solid/package.json | 2 +- examples/basic-server-svelte/package.json | 2 +- examples/basic-server-vanillajs/package.json | 2 +- examples/basic-server-vue/package.json | 2 +- examples/budget-allocator-server/package.json | 2 +- examples/cohort-heatmap-server/package.json | 2 +- examples/customer-segmentation-server/package.json | 2 +- examples/debug-server/package.json | 2 +- examples/lazy-auth-server/package.json | 2 +- examples/map-server/package.json | 2 +- examples/pdf-server/package.json | 2 +- examples/quickstart/package.json | 2 +- examples/say-server/package.json | 2 +- examples/scenario-modeler-server/package.json | 2 +- examples/shadertoy-server/package.json | 2 +- examples/sheet-music-server/package.json | 2 +- examples/system-monitor-server/package.json | 2 +- examples/threejs-server/package.json | 2 +- examples/transcript-server/package.json | 2 +- examples/video-resource-server/package.json | 2 +- examples/wiki-explorer-server/package.json | 2 +- package.json | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json index c88a306..60aa40d 100644 --- a/examples/basic-server-preact/package.json +++ b/examples/basic-server-preact/package.json @@ -5,7 +5,7 @@ "description": "Basic MCP App Server example using Preact", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/basic-server-preact" }, "license": "MIT", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index 07a9d3b..ea51a8a 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -5,7 +5,7 @@ "description": "Basic MCP App Server example using React", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/basic-server-react" }, "license": "MIT", diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json index a459ebc..b3ca909 100644 --- a/examples/basic-server-solid/package.json +++ b/examples/basic-server-solid/package.json @@ -5,7 +5,7 @@ "description": "Basic MCP App Server example using Solid", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/basic-server-solid" }, "license": "MIT", diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json index 3e0279f..19fe4c9 100644 --- a/examples/basic-server-svelte/package.json +++ b/examples/basic-server-svelte/package.json @@ -5,7 +5,7 @@ "description": "Basic MCP App Server example using Svelte", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/basic-server-svelte" }, "license": "MIT", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index 1404b65..ebfebbb 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -5,7 +5,7 @@ "description": "Basic MCP App Server example using vanilla JavaScript", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/basic-server-vanillajs" }, "license": "MIT", diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json index 32083c6..858dd89 100644 --- a/examples/basic-server-vue/package.json +++ b/examples/basic-server-vue/package.json @@ -5,7 +5,7 @@ "description": "Basic MCP App Server example using Vue", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/basic-server-vue" }, "license": "MIT", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index e44a147..6e41622 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -5,7 +5,7 @@ "description": "Budget allocator MCP App Server with interactive visualization", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/budget-allocator-server" }, "license": "MIT", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index de20f13..7e2d2e2 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -5,7 +5,7 @@ "description": "Cohort heatmap MCP App Server for retention analysis", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/cohort-heatmap-server" }, "license": "MIT", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index 643db17..bac3b89 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -5,7 +5,7 @@ "description": "Customer segmentation MCP App Server with filtering", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/customer-segmentation-server" }, "license": "MIT", diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json index 5be9291..e28f4d3 100644 --- a/examples/debug-server/package.json +++ b/examples/debug-server/package.json @@ -5,7 +5,7 @@ "description": "Debug MCP App Server for testing all SDK capabilities", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/debug-server" }, "license": "MIT", diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json index b12b29b..dbd4601 100644 --- a/examples/lazy-auth-server/package.json +++ b/examples/lazy-auth-server/package.json @@ -5,7 +5,7 @@ "description": "MCP App example demonstrating lazy (on-demand) OAuth: public tools work unauthenticated, protected tools return 401 + WWW-Authenticate so the host runs the OAuth flow only when needed", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/lazy-auth-server" }, "license": "MIT", diff --git a/examples/map-server/package.json b/examples/map-server/package.json index dc6a37f..cc4585f 100644 --- a/examples/map-server/package.json +++ b/examples/map-server/package.json @@ -5,7 +5,7 @@ "description": "MCP App Server example with CesiumJS 3D globe and geocoding", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/map-server" }, "license": "MIT", diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json index ae0870a..c8d9cf8 100644 --- a/examples/pdf-server/package.json +++ b/examples/pdf-server/package.json @@ -5,7 +5,7 @@ "description": "MCP server for loading and extracting text from PDF files with chunked pagination and interactive viewer", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/pdf-server" }, "license": "MIT", diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json index eecb080..57f8444 100644 --- a/examples/quickstart/package.json +++ b/examples/quickstart/package.json @@ -6,7 +6,7 @@ "description": "Quickstart MCP App Server example", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/quickstart" }, "license": "MIT", diff --git a/examples/say-server/package.json b/examples/say-server/package.json index cff7024..cc12249 100644 --- a/examples/say-server/package.json +++ b/examples/say-server/package.json @@ -5,7 +5,7 @@ "description": "Streaming TTS MCP App Server with karaoke-style text highlighting", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/say-server" }, "license": "MIT", diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index d82424d..e6f37c0 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -5,7 +5,7 @@ "description": "Financial scenario modeling MCP App Server", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/scenario-modeler-server" }, "license": "MIT", diff --git a/examples/shadertoy-server/package.json b/examples/shadertoy-server/package.json index 3e16a92..aec56c6 100644 --- a/examples/shadertoy-server/package.json +++ b/examples/shadertoy-server/package.json @@ -5,7 +5,7 @@ "description": "MCP App Server example for rendering ShaderToy-compatible GLSL shaders", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/shadertoy-server" }, "license": "MIT", diff --git a/examples/sheet-music-server/package.json b/examples/sheet-music-server/package.json index 1b233bb..85ca6bb 100644 --- a/examples/sheet-music-server/package.json +++ b/examples/sheet-music-server/package.json @@ -5,7 +5,7 @@ "description": "MCP App Server for rendering and playing sheet music from ABC notation", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/sheet-music-server" }, "license": "MIT", diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index f3a5763..2dc3ef1 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -5,7 +5,7 @@ "description": "System monitor MCP App Server with real-time stats", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/system-monitor-server" }, "license": "MIT", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index f8df10f..68864d9 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -5,7 +5,7 @@ "description": "Three.js 3D visualization MCP App Server", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/threejs-server" }, "license": "MIT", diff --git a/examples/transcript-server/package.json b/examples/transcript-server/package.json index 405f85f..46a8f98 100644 --- a/examples/transcript-server/package.json +++ b/examples/transcript-server/package.json @@ -5,7 +5,7 @@ "description": "MCP App Server for live speech transcription", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/transcript-server" }, "license": "MIT", diff --git a/examples/video-resource-server/package.json b/examples/video-resource-server/package.json index dea8fcb..4a7a61f 100644 --- a/examples/video-resource-server/package.json +++ b/examples/video-resource-server/package.json @@ -5,7 +5,7 @@ "description": "MCP App Server demonstrating video resources served as base64 blobs", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/video-resource-server" }, "license": "MIT", diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 0831670..52304cf 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -5,7 +5,7 @@ "description": "Wikipedia link explorer MCP App Server with graph visualization", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps", + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git", "directory": "examples/wiki-explorer-server" }, "license": "MIT", diff --git a/package.json b/package.json index bcea649..c86efa9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@modelcontextprotocol/ext-apps", "repository": { "type": "git", - "url": "https://github.com/modelcontextprotocol/ext-apps" + "url": "git+https://github.com/modelcontextprotocol/ext-apps.git" }, "homepage": "https://github.com/modelcontextprotocol/ext-apps", "version": "2.0.0",