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.
8.6 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).
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; 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;
@modelcontextprotocol/server stays optional and is only needed for the
./server helpers. Node.js 20+ is required.
Breaking changes
- Peer packages.
@modelcontextprotocol/sdk@^1is replaced by the split 2.x packages above, all at^2.0.0. Remove the 1.x package from your project: the two SDKs share no classes or types (a 1.xMcpErrororClientcannot 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 4.2+, ArkType, Valibot. zod 4.0 and 4.1 do not expose~standard.jsonSchemaand are rejected too. Raw zod shapes ({ q: z.string() }) still work withregisterAppToolbut are deprecated; wrap them withz.object({...}). App/AppBridgeextendProtocolfrom@modelcontextprotocol/client.ProtocolWithEventsis gone; use the SDK'sProtocol. TheAppRequest,AppNotificationandAppResultunions are kept as deprecated type aliases (nothing in 2.x consumes them) and will be removed in 3.0.- Handler context. Custom request handlers,
registerAppToolcallbacks (server) andapp.registerToolcallbacks (View) receive the SDK 2.x context:extra.signal→extra.mcpReq.signal,extra.requestId→extra.mcpReq.id;extra.sessionIdis unchanged andextra.authInfomoved toextra.http?.authInfo. setRequestHandler/setNotificationHandlertake method names.app.setRequestHandler(SomeRequestSchema, handler)becomesapp.setRequestHandler("some/method", { params: SomeParamsSchema }, (params, ctx) => …)for custom methods (the handler receives the parsed params); the two-argumentsetRequestHandler("tools/call", handler)form exists only for spec-defined method names. The 1.x(Schema, handler)form still works onAppandAppBridgeas 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-shapedextra(signal,requestId,sessionId,_meta,sendRequest,sendNotification,authInfo; exported asLegacyRequestHandlerExtra). It will be removed in 3.0.- Double-registration guard covers the SDK's own handlers. Registering a
handler for
notifications/progressornotifications/cancelled(which the baseProtocolalready owns) now throwsalready registered, aspingand theon*-owned methods did in 1.x. UseaddEventListeneror the SDK's progress callbacks instead of replacing these. - Errors. Remote JSON-RPC errors are
ProtocolError(numericcode); local conditions areSdkErrorwith a stringcode: request timeout →"REQUEST_TIMEOUT", connection closed →"CONNECTION_CLOSED". Cancelling a request with anAbortSignalalso rejects with"REQUEST_TIMEOUT"(the message is the abort reason). Messages no longer carry theMCP error N:prefix. A resource-not-found reply that carriesdata.uriarrives asResourceNotFoundError(aProtocolErrorsubclass,code-32602, with a.urigetter).
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.structuredContentis any JSON value (wastype: "object").McpUiToolResultNotification.params._metadocumentsio.modelcontextprotocol/serverInfoand no longer listsprogressToken/related-task(both still pass through).McpUiHostContext.toolInfo.tool.outputSchemais a loose object (only$schemais documented);inputSchema.propertiesvalues are now typed as JSON values.- A recursive JSON-value definition (
__schema0) is added under the$defsofMcpUiHostContext,McpUiHostContextChangedNotificationandMcpUiInitializeResult.
Checklist
npm uninstall @modelcontextprotocol/sdkand install the packages for your role from the table above.- Replace
sdk/...imports with the split packages (sdk/server/mcp.js→@modelcontextprotocol/server,sdk/server/streamableHttp.js→NodeStreamableHTTPServerTransportfrom@modelcontextprotocol/node,sdk/server/stdio.js→@modelcontextprotocol/server/stdio, hand-written Express routing →createMcpExpressAppfrom@modelcontextprotocol/express,sdk/types.js→@modelcontextprotocol/clientorserverfor the types,@modelcontextprotocol/corefor the zod schemas). - Wrap raw zod shapes with
z.object({...}). - Update custom handlers to the
extra.mcpReq.*context and method-keyedsetRequestHandlercalls. - Replace
McpError/ numeric-code checks withProtocolError/SdkError.