ext-apps/docs/migrate-to-2.md
Olivier Chafik 728b0bf38e 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.
2026-09-08 14:24:25 +01:00

6.5 KiB

title group description
Migrate to v2 Getting Started 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.