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.
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).
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).
| 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.
@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 share no classes or types (a 1.x McpError or
Client cannot be passed to 2.x code), even though they speak the same
wire protocol.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.jsonSchema and are
rejected too. 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.registerAppTool callbacks
(server) and app.registerTool callbacks (View) receive the SDK 2.x
context: extra.signal → extra.mcpReq.signal, extra.requestId →
extra.mcpReq.id; extra.sessionId is unchanged and extra.authInfo
moved to extra.http?.authInfo.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,
sendRequest, sendNotification, authInfo; exported as
LegacyRequestHandlerExtra). It will be removed in 3.0.notifications/progress or notifications/cancelled (which
the base Protocol already owns) now throws already registered, as
ping and the on*-owned methods did in 1.x. Use addEventListener or
the SDK's progress callbacks instead of replacing these.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.
A resource-not-found reply that carries data.uri arrives as
ResourceNotFoundError (a ProtocolError subclass, code -32602, with a
.uri getter).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.jsonThe 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.__schema0) is added under the $defs of
McpUiHostContext, McpUiHostContextChangedNotification and
McpUiInitializeResult.npm uninstall @modelcontextprotocol/sdk and install the packages for your
role from the table above.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, hand-written
Express routing → createMcpExpressApp from @modelcontextprotocol/express,
sdk/types.js → @modelcontextprotocol/client or server for the types,
@modelcontextprotocol/core for the zod schemas).z.object({...}).extra.mcpReq.* context and method-keyed
setRequestHandler calls.McpError / numeric-code checks with ProtocolError / SdkError.