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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 a required peer that `client` already depends on, so npm installs it for you).

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)
Expand Down
102 changes: 102 additions & 0 deletions docs/migrate-to-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
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 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.

## 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", { 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
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 <method>: …` |
| 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"`).
- `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

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` 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.
5. Replace `McpError` / numeric-code checks with `ProtocolError` / `SdkError`.
4 changes: 2 additions & 2 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
2 changes: 1 addition & 1 deletion docs/testing-mcp-apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-host/package.json
Original file line number Diff line number Diff line change
@@ -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\"",
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-server-preact/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-server-react/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-server-solid/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-server-svelte/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-server-vanillajs/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-server-vue/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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",
Expand Down
Loading
Loading