ext-apps/docs/migrate-to-2.md
Olivier Chafik 6d083cd69f 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.
2026-09-08 13:59:09 +01:00

6.1 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, 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.