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.
107 lines
6.5 KiB
Markdown
107 lines
6.5 KiB
Markdown
---
|
|
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` 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`.
|
|
- **`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. 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
|
|
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`.
|