Skip to content

Commit c8bc264

Browse files
refactor: eliminate sdk-compat.ts via isSpecType() (typescript-sdk#1887)
- spec.types.ts: import RequestId from @modelcontextprotocol/client (already public) - app.ts/app-bridge.ts: replace pass-through *Schema shims with z.custom<T>(v => isSpecType('T', v)) - generate-schemas.ts: emit isSpecType-backed z.custom for external SDK types instead of importing from sdk-compat - delete src/sdk-compat.ts Friction surfaced: ExtensionHandle.setRequestHandler/sendRequest accept AnySchema (Zod-only), so specTypeSchema()'s StandardSchemaV1 return cannot be passed directly. Wrapped via z.custom(isSpecType) instead. #1868 should widen the param type to StandardSchemaV1 | AnySchema.
1 parent 76b3ba7 commit c8bc264

File tree

9 files changed

+76
-155
lines changed

9 files changed

+76
-155
lines changed

docs/quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export function createServer(): McpServer {
242242
```ts source="../examples/quickstart/main.ts"
243243
import { createMcpExpressApp } from "@modelcontextprotocol/express";
244244
import type { McpServer } from "@modelcontextprotocol/server";
245-
import { StdioServerTransport } from "@modelcontextprotocol/server";
245+
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
246246
import { NodeStreamableHTTPServerTransport as StreamableHTTPServerTransport } from "@modelcontextprotocol/node";
247247
import cors from "cors";
248248
import type { Request, Response } from "express";

package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"homepage": "https://github.com/modelcontextprotocol/ext-apps",
88
"version": "1.5.0",
99
"license": "MIT",
10-
"description": "MCP Apps SDK \u2014 Enable MCP servers to display interactive user interfaces in conversational clients.",
10+
"description": "MCP Apps SDK Enable MCP servers to display interactive user interfaces in conversational clients.",
1111
"type": "module",
1212
"engines": {
1313
"node": ">=20"
@@ -77,6 +77,10 @@
7777
"author": "Olivier Chafik",
7878
"devDependencies": {
7979
"@boneskull/typedoc-plugin-mermaid": "^0.2.0",
80+
"@modelcontextprotocol/client": "file:../../../../../tmp/modelcontextprotocol-client-2.0.0-alpha.2.tgz",
81+
"@modelcontextprotocol/express": "file:../../../../../tmp/modelcontextprotocol-express-2.0.0-alpha.2.tgz",
82+
"@modelcontextprotocol/node": "file:../../../../../tmp/modelcontextprotocol-node-2.0.0-alpha.2.tgz",
83+
"@modelcontextprotocol/server": "file:../../../../../tmp/modelcontextprotocol-server-2.0.0-alpha.2.tgz",
8084
"@playwright/test": "1.57.0",
8185
"@types/bun": "^1.3.2",
8286
"@types/node": "20.19.27",
@@ -104,18 +108,14 @@
104108
"typedoc": "^0.28.14",
105109
"typedoc-github-theme": "^0.4.0",
106110
"typescript": "^5.9.3",
107-
"zod": "^4.1.13",
108-
"@modelcontextprotocol/client": "file:/tmp/modelcontextprotocol-client-2.0.0-alpha.2.tgz",
109-
"@modelcontextprotocol/server": "file:/tmp/modelcontextprotocol-server-2.0.0-alpha.2.tgz",
110-
"@modelcontextprotocol/express": "file:/tmp/modelcontextprotocol-express-2.0.0-alpha.2.tgz",
111-
"@modelcontextprotocol/node": "file:/tmp/modelcontextprotocol-node-2.0.0-alpha.2.tgz"
111+
"zod": "^4.1.13"
112112
},
113113
"peerDependencies": {
114+
"@modelcontextprotocol/client": "file:../../../../../tmp/modelcontextprotocol-client-2.0.0-alpha.2.tgz",
115+
"@modelcontextprotocol/server": "file:../../../../../tmp/modelcontextprotocol-server-2.0.0-alpha.2.tgz",
114116
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
115117
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
116-
"zod": "^3.25.0 || ^4.0.0",
117-
"@modelcontextprotocol/client": "^2.0.0-alpha",
118-
"@modelcontextprotocol/server": "^2.0.0-alpha"
118+
"zod": "^3.25.0 || ^4.0.0"
119119
},
120120
"peerDependenciesMeta": {
121121
"react": {

scripts/generate-schemas.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,22 @@ function postProcess(content: string): string {
187187
// 1. Rewrite to zod/v4 and add MCP SDK schema imports.
188188
// zod/v4 aligns with the SDK's own zod import — composing v3 and v4
189189
// schema instances throws at parse time. See header comment for details.
190-
const mcpImports = EXTERNAL_TYPE_SCHEMAS.join(",\n ");
190+
const typeImports = EXTERNAL_TYPE_SCHEMAS.map((s) =>
191+
s.replace(/Schema$/, ""),
192+
).join(", ");
191193
content = content.replace(
192194
'import { z } from "zod";',
193195
`import { z } from "zod/v4";
194-
import {
195-
${mcpImports},
196-
} from "../sdk-compat.js";`,
196+
import { isSpecType } from "@modelcontextprotocol/client";
197+
import type { ${typeImports} } from "@modelcontextprotocol/client";`,
197198
);
198199

199-
// 2. Remove z.any() placeholders for external types (now imported from MCP SDK)
200+
// 2. Replace z.any() placeholders for external SDK types with isSpecType-backed z.custom
200201
for (const schema of EXTERNAL_TYPE_SCHEMAS) {
202+
const typeName = schema.replace(/Schema$/, "");
201203
content = content.replace(
202-
new RegExp(`(?:export )?const ${schema} = z\\.any\\(\\);\\n?`, "g"),
203-
"",
204+
new RegExp(`((?:export )?const ${schema}) = z\\.any\\(\\);`, "g"),
205+
`$1 = z.custom<${typeName}>((v) => isSpecType("${typeName}", v));`,
204206
);
205207
}
206208

src/app-bridge.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ import {
2626
} from "@modelcontextprotocol/server";
2727

2828
import { EventDispatcher } from "./events";
29-
import {
30-
CallToolResultSchema,
31-
ListToolsResultSchema,
32-
} from "./sdk-compat";
29+
import { isSpecType } from "@modelcontextprotocol/server";
3330
import {
3431
LATEST_PROTOCOL_VERSION,
3532
McpUiAppCapabilities,
@@ -637,7 +634,7 @@ export class AppBridge extends EventDispatcher<AppBridgeEventMap> {
637634
return this.ui.sendRequest(
638635
"ui/call-view-tool",
639636
params,
640-
CallToolResultSchema,
637+
z.custom<CallToolResult>((v) => isSpecType("CallToolResult", v)),
641638
options,
642639
);
643640
}
@@ -651,7 +648,7 @@ export class AppBridge extends EventDispatcher<AppBridgeEventMap> {
651648
return this.ui.sendRequest(
652649
"ui/list-view-tools",
653650
params,
654-
ListToolsResultSchema,
651+
z.custom<ListToolsResult>((v) => isSpecType("ListToolsResult", v)),
655652
options,
656653
);
657654
}

src/app.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,8 @@ import {
2020
import { EventDispatcher } from "./events";
2121
export { EventDispatcher, ProtocolWithEvents } from "./events";
2222
import { PostMessageTransport } from "./message-transport";
23-
import {
24-
CallToolRequestParamsSchema,
25-
CallToolResultSchema,
26-
EmptyResultSchema,
27-
ListToolsRequestParamsSchema,
28-
ListToolsResultSchema,
29-
} from "./sdk-compat";
23+
import { isSpecType } from "@modelcontextprotocol/client";
24+
import { z } from "zod/v4";
3025
import {
3126
LATEST_PROTOCOL_VERSION,
3227
McpUiAppCapabilities,
@@ -231,15 +226,19 @@ export class App extends EventDispatcher<AppEventMap> {
231226
// Non-spec host→iframe tool surface (renamed from tools/call & tools/list).
232227
this.ui.setRequestHandler(
233228
"ui/call-view-tool",
234-
CallToolRequestParamsSchema,
229+
z.custom<CallToolRequest["params"]>((v) =>
230+
isSpecType("CallToolRequestParams", v),
231+
),
235232
async (params, ctx) => {
236233
if (!this._oncalltool) throw new Error("No oncalltool handler set");
237234
return this._oncalltool(params, toExtra(ctx));
238235
},
239236
);
240237
this.ui.setRequestHandler(
241238
"ui/list-view-tools",
242-
ListToolsRequestParamsSchema,
239+
z.custom<ListToolsRequest["params"]>((v) =>
240+
v === undefined || isSpecType("PaginatedRequestParams", v),
241+
),
243242
async (params, ctx) => {
244243
if (!this._onlisttools) throw new Error("No onlisttools handler set");
245244
return this._onlisttools(params, toExtra(ctx));
@@ -431,7 +430,7 @@ export class App extends EventDispatcher<AppEventMap> {
431430
return this.ui.sendRequest(
432431
"ui/update-model-context",
433432
params,
434-
EmptyResultSchema,
433+
z.custom<Record<string, never>>((v) => isSpecType("EmptyResult", v)),
435434
options,
436435
);
437436
}

src/generated/schema.ts

Lines changed: 34 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/sdk-compat.ts

Lines changed: 0 additions & 102 deletions
This file was deleted.

src/spec.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
* @see https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx
1111
*/
1212

13-
import type { RequestId } from "./sdk-compat.js";
1413
import type {
14+
RequestId,
1515
CallToolResult,
1616
ContentBlock,
1717
EmbeddedResource,

0 commit comments

Comments
 (0)