Skip to content

Commit ab3c9c3

Browse files
feat: port to v2 concrete-Protocol (#1891) — events shim + sdk-schemas
- events.ts: ProtocolWithEvents extends Protocol (concrete); v1-compat envelope-schema overload extracts method+params from .shape and forwards to v2's 3-arg form. Typed via EnvelopeRequestHandler/ParamsOf<T>. Event dispatcher writes _notificationHandlers directly to preserve the v1 sync-dispatch contract (v2's await parseSchema introduces a microtask). - sdk-schemas.ts (new): envelope/result stubs for SDK *Schema constants via specTypeSchema(); typed Env<M,P> so ParamsOf infers concretely. - app.ts/app-bridge.ts: redirect *Schema imports to ./sdk-schemas; delete no-op assert*Capability overrides; convert this.request({method, params}, Schema) → this.request('method', params, Schema); autowire block uses client.callTool/listResources/etc + 2-arg setNotificationHandler. - generate-schemas.ts: emit z.custom<T>(isSpecType('T', v)) for SDK external types instead of importing dropped *Schema constants. - message-transport.ts: JSONRPCMessageSchema → parseJSONRPCMessage. - server/index.test.ts: Pick<McpServer> → as McpServer. tsc 0, build 0, npm test 276/2/0. app-bridge.test.ts: 88/88 pass.
1 parent aa326fe commit ab3c9c3

11 files changed

Lines changed: 399 additions & 1929 deletions

File tree

package-lock.json

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

scripts/generate-schemas.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,24 @@ 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(",\n ");
191193
content = content.replace(
192194
'import { z } from "zod";',
193195
`import { z } from "zod/v4";
194-
import {
195-
${mcpImports},
196-
} from "@modelcontextprotocol/sdk/types.js";`,
196+
import { isSpecType } from "@modelcontextprotocol/client";
197+
import type {
198+
${typeImports},
199+
} from "@modelcontextprotocol/client";`,
197200
);
198201

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

src/app-bridge.test.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
ReadResourceResultSchema,
1212
ResourceListChangedNotificationSchema,
1313
ToolListChangedNotificationSchema,
14-
} from "@modelcontextprotocol/client";
14+
} from "./sdk-schemas";
1515

1616
import { App } from "./app";
1717
import {
@@ -681,7 +681,8 @@ describe("App <-> AppBridge integration", () => {
681681

682682
// Bridge can send ping via the protocol's request method
683683
const result = await bridge.request(
684-
{ method: "ping", params: {} },
684+
"ping",
685+
{},
685686
EmptyResultSchema,
686687
);
687688

@@ -795,7 +796,8 @@ describe("App <-> AppBridge integration", () => {
795796

796797
// App sends resources/list request via the protocol's request method
797798
const result = await app.request(
798-
{ method: "resources/list", params: requestParams },
799+
"resources/list",
800+
requestParams,
799801
ListResourcesResultSchema,
800802
);
801803

@@ -839,7 +841,8 @@ describe("App <-> AppBridge integration", () => {
839841
await app.connect(appTransport);
840842

841843
const result = await app.request(
842-
{ method: "resources/read", params: requestParams },
844+
"resources/read",
845+
requestParams,
843846
ReadResourceResultSchema,
844847
);
845848

@@ -890,7 +893,8 @@ describe("App <-> AppBridge integration", () => {
890893
await app.connect(appTransport);
891894

892895
const result = await app.request(
893-
{ method: "resources/templates/list", params: requestParams },
896+
"resources/templates/list",
897+
requestParams,
894898
ListResourceTemplatesResultSchema,
895899
);
896900

@@ -913,7 +917,8 @@ describe("App <-> AppBridge integration", () => {
913917
await app.connect(appTransport);
914918

915919
const result = await app.request(
916-
{ method: "prompts/list", params: requestParams },
920+
"prompts/list",
921+
requestParams,
917922
ListPromptsResultSchema,
918923
);
919924

@@ -1370,13 +1375,11 @@ describe("isToolVisibilityAppOnly", () => {
13701375
testHostCapabilities,
13711376
);
13721377
bridge2.setRequestHandler(
1373-
// @ts-expect-error — exercising throw path with raw schema
13741378
{ shape: { method: { value: "test/method" } } },
13751379
() => ({}),
13761380
);
13771381
expect(() => {
13781382
bridge2.setRequestHandler(
1379-
// @ts-expect-error — exercising throw path with raw schema
13801383
{ shape: { method: { value: "test/method" } } },
13811384
() => ({}),
13821385
);
@@ -1388,7 +1391,6 @@ describe("isToolVisibilityAppOnly", () => {
13881391
app2.addEventListener("toolinput", () => {});
13891392
expect(() => {
13901393
app2.setNotificationHandler(
1391-
// @ts-expect-error — exercising throw path with raw schema
13921394
{
13931395
shape: { method: { value: "ui/notifications/tool-input" } },
13941396
},

src/app-bridge.ts

Lines changed: 35 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,44 @@ import { Client } from "@modelcontextprotocol/client";
22
import { Transport } from "@modelcontextprotocol/client";
33
import {
44
CallToolRequest,
5-
CallToolRequestSchema,
65
CallToolResult,
7-
CallToolResultSchema,
86
EmptyResult,
97
Implementation,
108
ListPromptsRequest,
11-
ListPromptsRequestSchema,
129
ListPromptsResult,
13-
ListPromptsResultSchema,
1410
ListResourcesRequest,
15-
ListResourcesRequestSchema,
1611
ListResourcesResult,
17-
ListResourcesResultSchema,
1812
ListResourceTemplatesRequest,
19-
ListResourceTemplatesRequestSchema,
2013
ListResourceTemplatesResult,
21-
ListResourceTemplatesResultSchema,
2214
ListToolsRequest,
23-
ListToolsRequestSchema,
24-
ListToolsResultSchema,
2515
LoggingMessageNotification,
26-
LoggingMessageNotificationSchema,
2716
PingRequest,
28-
PingRequestSchema,
2917
PromptListChangedNotification,
30-
PromptListChangedNotificationSchema,
3118
ReadResourceRequest,
32-
ReadResourceRequestSchema,
3319
ReadResourceResult,
34-
ReadResourceResultSchema,
3520
ResourceListChangedNotification,
36-
ResourceListChangedNotificationSchema,
3721
Tool,
3822
ToolListChangedNotification,
39-
ToolListChangedNotificationSchema,
4023
} from "@modelcontextprotocol/client";
24+
import {
25+
CallToolRequestSchema,
26+
CallToolResultSchema,
27+
ListPromptsRequestSchema,
28+
ListPromptsResultSchema,
29+
ListResourcesRequestSchema,
30+
ListResourcesResultSchema,
31+
ListResourceTemplatesRequestSchema,
32+
ListResourceTemplatesResultSchema,
33+
ListToolsRequestSchema,
34+
ListToolsResultSchema,
35+
LoggingMessageNotificationSchema,
36+
PingRequestSchema,
37+
PromptListChangedNotificationSchema,
38+
ReadResourceRequestSchema,
39+
ReadResourceResultSchema,
40+
ResourceListChangedNotificationSchema,
41+
ToolListChangedNotificationSchema,
42+
} from "./sdk-schemas";
4143
import {
4244
ProtocolOptions,
4345
RequestOptions,
@@ -360,7 +362,7 @@ export class AppBridge extends ProtocolWithEvents<
360362
this._hostContext = options?.hostContext || {};
361363

362364
this.setRequestHandler(McpUiInitializeRequestSchema, (request) =>
363-
this._oninitialize(request),
365+
this._oninitialize(request as McpUiInitializeRequest),
364366
);
365367

366368
this.setRequestHandler(PingRequestSchema, (request, extra) => {
@@ -1312,46 +1314,6 @@ export class AppBridge extends ProtocolWithEvents<
13121314
return this.notification("notifications/prompts/list_changed", params);
13131315
}
13141316

1315-
/**
1316-
* Verify that the guest supports the capability required for the given request method.
1317-
* @internal
1318-
*/
1319-
assertCapabilityForMethod(method: AppRequest["method"]): void {
1320-
// TODO
1321-
}
1322-
1323-
/**
1324-
* Verify that a request handler is registered and supported for the given method.
1325-
* @internal
1326-
*/
1327-
assertRequestHandlerCapability(method: AppRequest["method"]): void {
1328-
// TODO
1329-
}
1330-
1331-
/**
1332-
* Verify that the host supports the capability required for the given notification method.
1333-
* @internal
1334-
*/
1335-
assertNotificationCapability(method: AppNotification["method"]): void {
1336-
// TODO
1337-
}
1338-
1339-
/**
1340-
* Verify that task creation is supported for the given request method.
1341-
* @internal
1342-
*/
1343-
protected assertTaskCapability(_method: string): void {
1344-
throw new Error("Tasks are not supported in MCP Apps");
1345-
}
1346-
1347-
/**
1348-
* Verify that task handler is supported for the given method.
1349-
* @internal
1350-
*/
1351-
protected assertTaskHandlerCapability(_method: string): void {
1352-
throw new Error("Task handlers are not supported in MCP Apps");
1353-
}
1354-
13551317
/**
13561318
* Get the host capabilities passed to the constructor.
13571319
*
@@ -1731,60 +1693,37 @@ export class AppBridge extends ProtocolWithEvents<
17311693
}
17321694

17331695
if (serverCapabilities.tools) {
1734-
this.oncalltool = async (params, extra) => {
1735-
return this._client!.request(
1736-
{ method: "tools/call", params },
1737-
CallToolResultSchema,
1738-
{ signal: extra.signal },
1739-
);
1740-
};
1696+
this.oncalltool = async (params, extra) =>
1697+
this._client!.callTool(params, { signal: extra.signal });
17411698
if (serverCapabilities.tools.listChanged) {
17421699
this._client.setNotificationHandler(
1743-
ToolListChangedNotificationSchema,
1700+
"notifications/tools/list_changed",
17441701
(n) => this.sendToolListChanged(n.params),
17451702
);
17461703
}
17471704
}
17481705
if (serverCapabilities.resources) {
1749-
this.onlistresources = async (params, extra) => {
1750-
return this._client!.request(
1751-
{ method: "resources/list", params },
1752-
ListResourcesResultSchema,
1753-
{ signal: extra.signal },
1754-
);
1755-
};
1756-
this.onlistresourcetemplates = async (params, extra) => {
1757-
return this._client!.request(
1758-
{ method: "resources/templates/list", params },
1759-
ListResourceTemplatesResultSchema,
1760-
{ signal: extra.signal },
1761-
);
1762-
};
1763-
this.onreadresource = async (params, extra) => {
1764-
return this._client!.request(
1765-
{ method: "resources/read", params },
1766-
ReadResourceResultSchema,
1767-
{ signal: extra.signal },
1768-
);
1769-
};
1706+
this.onlistresources = async (params, extra) =>
1707+
this._client!.listResources(params, { signal: extra.signal });
1708+
this.onlistresourcetemplates = async (params, extra) =>
1709+
this._client!.listResourceTemplates(params, {
1710+
signal: extra.signal,
1711+
});
1712+
this.onreadresource = async (params, extra) =>
1713+
this._client!.readResource(params, { signal: extra.signal });
17701714
if (serverCapabilities.resources.listChanged) {
17711715
this._client.setNotificationHandler(
1772-
ResourceListChangedNotificationSchema,
1716+
"notifications/resources/list_changed",
17731717
(n) => this.sendResourceListChanged(n.params),
17741718
);
17751719
}
17761720
}
17771721
if (serverCapabilities.prompts) {
1778-
this.onlistprompts = async (params, extra) => {
1779-
return this._client!.request(
1780-
{ method: "prompts/list", params },
1781-
ListPromptsResultSchema,
1782-
{ signal: extra.signal },
1783-
);
1784-
};
1722+
this.onlistprompts = async (params, extra) =>
1723+
this._client!.listPrompts(params, { signal: extra.signal });
17851724
if (serverCapabilities.prompts.listChanged) {
17861725
this._client.setNotificationHandler(
1787-
PromptListChangedNotificationSchema,
1726+
"notifications/prompts/list_changed",
17881727
(n) => this.sendPromptListChanged(n.params),
17891728
);
17901729
}

0 commit comments

Comments
 (0)