Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
"author": "Olivier Chafik",
"devDependencies": {
"@boneskull/typedoc-plugin-mermaid": "^0.2.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"@playwright/test": "1.57.0",
"@types/bun": "^1.3.2",
"@types/node": "20.19.27",
Expand Down Expand Up @@ -107,7 +106,6 @@
"zod": "^4.1.13"
},
"peerDependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
"zod": "^3.25.0 || ^4.0.0"
Expand All @@ -125,5 +123,11 @@
"seroval-plugins": "1.4.2",
"solid-js": "1.9.10",
"@types/node": "20.19.27"
},
"dependencies": {
"@modelcontextprotocol/client": "file:../../../../../tmp/modelcontextprotocol-client-2.0.0-alpha.2.tgz",
"@modelcontextprotocol/express": "file:../../../../../tmp/modelcontextprotocol-express-2.0.0-alpha.2.tgz",
"@modelcontextprotocol/node": "file:../../../../../tmp/modelcontextprotocol-node-2.0.0-alpha.2.tgz",
"@modelcontextprotocol/server": "file:../../../../../tmp/modelcontextprotocol-server-2.0.0-alpha.2.tgz"
}
}
18 changes: 11 additions & 7 deletions scripts/generate-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,20 +187,24 @@ function postProcess(content: string): string {
// 1. Rewrite to zod/v4 and add MCP SDK schema imports.
// zod/v4 aligns with the SDK's own zod import — composing v3 and v4
// schema instances throws at parse time. See header comment for details.
const mcpImports = EXTERNAL_TYPE_SCHEMAS.join(",\n ");
const typeImports = EXTERNAL_TYPE_SCHEMAS.map((s) =>
s.replace(/Schema$/, ""),
).join(",\n ");
content = content.replace(
'import { z } from "zod";',
`import { z } from "zod/v4";
import {
${mcpImports},
} from "@modelcontextprotocol/sdk/types.js";`,
import { isSpecType } from "@modelcontextprotocol/client";
import type {
${typeImports},
} from "@modelcontextprotocol/client";`,
);

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

Expand Down
6 changes: 3 additions & 3 deletions src/app-bridge.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
* @module
*/

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import { Client } from "@modelcontextprotocol/client";
import type { Transport } from "@modelcontextprotocol/client";
import {
CallToolResult,
CallToolResultSchema,
ListResourcesResultSchema,
ReadResourceResultSchema,
ListPromptsResultSchema,
} from "@modelcontextprotocol/sdk/types.js";
} from "@modelcontextprotocol/client";
import { AppBridge, PostMessageTransport } from "./app-bridge.js";
import type { McpUiDisplayMode } from "./types.js";

Expand Down
30 changes: 15 additions & 15 deletions src/app-bridge.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
import type { ServerCapabilities } from "@modelcontextprotocol/sdk/types.js";
import {
InMemoryTransport,
type Client,
type ServerCapabilities,
} from "@modelcontextprotocol/client";
import {
EmptyResultSchema,
ListPromptsResultSchema,
Expand All @@ -11,7 +13,7 @@ import {
ReadResourceResultSchema,
ResourceListChangedNotificationSchema,
ToolListChangedNotificationSchema,
} from "@modelcontextprotocol/sdk/types.js";
} from "./sdk-schemas";

import { App } from "./app";
import {
Expand Down Expand Up @@ -680,10 +682,7 @@ describe("App <-> AppBridge integration", () => {
await app.connect(appTransport);

// Bridge can send ping via the protocol's request method
const result = await bridge.request(
{ method: "ping", params: {} },
EmptyResultSchema,
);
const result = await bridge.request("ping", {}, EmptyResultSchema);

expect(result).toEqual({});
});
Expand Down Expand Up @@ -795,7 +794,8 @@ describe("App <-> AppBridge integration", () => {

// App sends resources/list request via the protocol's request method
const result = await app.request(
{ method: "resources/list", params: requestParams },
"resources/list",
requestParams,
ListResourcesResultSchema,
);

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

const result = await app.request(
{ method: "resources/read", params: requestParams },
"resources/read",
requestParams,
ReadResourceResultSchema,
);

Expand Down Expand Up @@ -890,7 +891,8 @@ describe("App <-> AppBridge integration", () => {
await app.connect(appTransport);

const result = await app.request(
{ method: "resources/templates/list", params: requestParams },
"resources/templates/list",
requestParams,
ListResourceTemplatesResultSchema,
);

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

const result = await app.request(
{ method: "prompts/list", params: requestParams },
"prompts/list",
requestParams,
ListPromptsResultSchema,
);

Expand Down Expand Up @@ -1370,13 +1373,11 @@ describe("isToolVisibilityAppOnly", () => {
testHostCapabilities,
);
bridge2.setRequestHandler(
// @ts-expect-error — exercising throw path with raw schema
{ shape: { method: { value: "test/method" } } },
() => ({}),
);
expect(() => {
bridge2.setRequestHandler(
// @ts-expect-error — exercising throw path with raw schema
{ shape: { method: { value: "test/method" } } },
() => ({}),
);
Expand All @@ -1388,7 +1389,6 @@ describe("isToolVisibilityAppOnly", () => {
app2.addEventListener("toolinput", () => {});
expect(() => {
app2.setNotificationHandler(
// @ts-expect-error — exercising throw path with raw schema
{
shape: { method: { value: "ui/notifications/tool-input" } },
},
Expand Down
Loading
Loading