api: optional Client in AppBridge (allow custom forwarding), better protocol types (AppRequest, AppNotification, AppResult) (#146)
* Add setter-based handlers for MCP request/notification forwarding - Add oncalltool, onlistresources, onlistresourcetemplates, onreadresource, onlistprompts setters for Guest UI → Host requests - Add sendToolListChanged, sendResourceListChanged, sendPromptListChanged methods for Host → Guest UI notifications - Refactor connect() to use new setters with inlined callbacks - Update documentation to clarify optional client parameter behavior - Remove unused forwardRequest/forwardNotification helper methods - Add comprehensive tests for AppBridge without MCP client (manual handlers) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Potential fix for pull request finding 'Unused variable, import, function or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: move prettier to dependencies for git-based installs * fix: add explicit prettierrc to prevent inheriting parent configs during git-based installs * Apply suggestions from code review Co-authored-by: Jonathan Hefner <jonathan@hefner.pro> * refactor(tests): factor out test values and add missing assertions Address PR review feedback: - Factor out test value objects (toolCall, resultContent, etc.) for clearer tests - Add missing assertions on receivedRequests[0] for list handlers - Improve test readability by defining expected values at the top of each test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: improve TypeDoc formatting and use `as const` for notification methods - Use proper TypeDoc syntax with `@param callback.params` and `@param callback.extra` - Add {@link ResultType} references for return types - Replace `as Notification` with `as const` on method literals where possible (sendResourceListChanged, sendPromptListChanged) - Simplify sendHostContextChange cast syntax 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(types): use explicit type unions for AppBridge protocol types - Add AppBridgeRequest, AppBridgeNotification, AppBridgeResult type unions that explicitly list all valid types for the protocol - Replace generic SDK Request/Notification/Result with our specific types - Remove all type casts by using `as const` on method literals - Add index signature to McpUiHostContext for forward compatibility This improves type safety and documents exactly what the AppBridge supports. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: move AppRequest/AppNotification/AppResult to types.ts Move protocol type unions from app-bridge.ts to types.ts where they belong with other type definitions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: regenerate schemas with clean npm install 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
This commit is contained in:
parent
c9e37204de
commit
54344552b7
9 changed files with 749 additions and 103 deletions
7
.prettierrc.json
Normal file
7
.prettierrc.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": false,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2
|
||||
}
|
||||
|
|
@ -64,7 +64,6 @@
|
|||
"express": "^5.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"nodemon": "^3.1.0",
|
||||
"prettier": "^3.6.2",
|
||||
"ts-to-zod": "^5.1.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typedoc": "^0.28.14",
|
||||
|
|
@ -75,6 +74,7 @@
|
|||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": "^3.6.2",
|
||||
"@modelcontextprotocol/sdk": "^1.24.3",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,16 @@ 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 { EmptyResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
||||
import {
|
||||
EmptyResultSchema,
|
||||
ListPromptsResultSchema,
|
||||
ListResourcesResultSchema,
|
||||
ListResourceTemplatesResultSchema,
|
||||
PromptListChangedNotificationSchema,
|
||||
ReadResourceResultSchema,
|
||||
ResourceListChangedNotificationSchema,
|
||||
ToolListChangedNotificationSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
|
||||
import { App } from "./app";
|
||||
import { AppBridge, type McpUiHostCapabilities } from "./app-bridge";
|
||||
|
|
@ -508,4 +517,193 @@ describe("App <-> AppBridge integration", () => {
|
|||
expect(result).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AppBridge without MCP client (manual handlers)", () => {
|
||||
let app: App;
|
||||
let bridge: AppBridge;
|
||||
let appTransport: InMemoryTransport;
|
||||
let bridgeTransport: InMemoryTransport;
|
||||
|
||||
beforeEach(() => {
|
||||
[appTransport, bridgeTransport] = InMemoryTransport.createLinkedPair();
|
||||
app = new App(testAppInfo, {}, { autoResize: false });
|
||||
// Pass null instead of a client - manual handler registration
|
||||
bridge = new AppBridge(null, testHostInfo, testHostCapabilities);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await appTransport.close();
|
||||
await bridgeTransport.close();
|
||||
});
|
||||
|
||||
it("connect() works without client", async () => {
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
// Initialization should still work
|
||||
const hostInfo = app.getHostVersion();
|
||||
expect(hostInfo).toEqual(testHostInfo);
|
||||
});
|
||||
|
||||
it("oncalltool setter registers handler for tools/call requests", async () => {
|
||||
const toolCall = { name: "test-tool", arguments: { arg: "value" } };
|
||||
const resultContent = [{ type: "text" as const, text: "result" }];
|
||||
const receivedCalls: unknown[] = [];
|
||||
|
||||
bridge.oncalltool = async (params) => {
|
||||
receivedCalls.push(params);
|
||||
return { content: resultContent };
|
||||
};
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
// App calls a tool via callServerTool
|
||||
const result = await app.callServerTool(toolCall);
|
||||
|
||||
expect(receivedCalls).toHaveLength(1);
|
||||
expect(receivedCalls[0]).toMatchObject(toolCall);
|
||||
expect(result.content).toEqual(resultContent);
|
||||
});
|
||||
|
||||
it("onlistresources setter registers handler for resources/list requests", async () => {
|
||||
const requestParams = {};
|
||||
const resources = [{ uri: "test://resource", name: "Test" }];
|
||||
const receivedRequests: unknown[] = [];
|
||||
|
||||
bridge.onlistresources = async (params) => {
|
||||
receivedRequests.push(params);
|
||||
return { resources };
|
||||
};
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
// App sends resources/list request via the protocol's request method
|
||||
const result = await app.request(
|
||||
{ method: "resources/list", params: requestParams },
|
||||
ListResourcesResultSchema,
|
||||
);
|
||||
|
||||
expect(receivedRequests).toHaveLength(1);
|
||||
expect(receivedRequests[0]).toMatchObject(requestParams);
|
||||
expect(result.resources).toEqual(resources);
|
||||
});
|
||||
|
||||
it("onreadresource setter registers handler for resources/read requests", async () => {
|
||||
const requestParams = { uri: "test://resource" };
|
||||
const contents = [{ uri: "test://resource", text: "content" }];
|
||||
const receivedRequests: unknown[] = [];
|
||||
|
||||
bridge.onreadresource = async (params) => {
|
||||
receivedRequests.push(params);
|
||||
return { contents: [{ uri: params.uri, text: "content" }] };
|
||||
};
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
const result = await app.request(
|
||||
{ method: "resources/read", params: requestParams },
|
||||
ReadResourceResultSchema,
|
||||
);
|
||||
|
||||
expect(receivedRequests).toHaveLength(1);
|
||||
expect(receivedRequests[0]).toMatchObject(requestParams);
|
||||
expect(result.contents).toEqual(contents);
|
||||
});
|
||||
|
||||
it("onlistresourcetemplates setter registers handler for resources/templates/list requests", async () => {
|
||||
const requestParams = {};
|
||||
const resourceTemplates = [
|
||||
{ uriTemplate: "test://{id}", name: "Test Template" },
|
||||
];
|
||||
const receivedRequests: unknown[] = [];
|
||||
|
||||
bridge.onlistresourcetemplates = async (params) => {
|
||||
receivedRequests.push(params);
|
||||
return { resourceTemplates };
|
||||
};
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
const result = await app.request(
|
||||
{ method: "resources/templates/list", params: requestParams },
|
||||
ListResourceTemplatesResultSchema,
|
||||
);
|
||||
|
||||
expect(receivedRequests).toHaveLength(1);
|
||||
expect(receivedRequests[0]).toMatchObject(requestParams);
|
||||
expect(result.resourceTemplates).toEqual(resourceTemplates);
|
||||
});
|
||||
|
||||
it("onlistprompts setter registers handler for prompts/list requests", async () => {
|
||||
const requestParams = {};
|
||||
const prompts = [{ name: "test-prompt" }];
|
||||
const receivedRequests: unknown[] = [];
|
||||
|
||||
bridge.onlistprompts = async (params) => {
|
||||
receivedRequests.push(params);
|
||||
return { prompts };
|
||||
};
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
const result = await app.request(
|
||||
{ method: "prompts/list", params: requestParams },
|
||||
ListPromptsResultSchema,
|
||||
);
|
||||
|
||||
expect(receivedRequests).toHaveLength(1);
|
||||
expect(receivedRequests[0]).toMatchObject(requestParams);
|
||||
expect(result.prompts).toEqual(prompts);
|
||||
});
|
||||
|
||||
it("sendToolListChanged sends notification to app", async () => {
|
||||
const receivedNotifications: unknown[] = [];
|
||||
app.setNotificationHandler(ToolListChangedNotificationSchema, (n) => {
|
||||
receivedNotifications.push(n.params);
|
||||
});
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
bridge.sendToolListChanged();
|
||||
await flush();
|
||||
|
||||
expect(receivedNotifications).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("sendResourceListChanged sends notification to app", async () => {
|
||||
const receivedNotifications: unknown[] = [];
|
||||
app.setNotificationHandler(ResourceListChangedNotificationSchema, (n) => {
|
||||
receivedNotifications.push(n.params);
|
||||
});
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
bridge.sendResourceListChanged();
|
||||
await flush();
|
||||
|
||||
expect(receivedNotifications).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("sendPromptListChanged sends notification to app", async () => {
|
||||
const receivedNotifications: unknown[] = [];
|
||||
app.setNotificationHandler(PromptListChangedNotificationSchema, (n) => {
|
||||
receivedNotifications.push(n.params);
|
||||
});
|
||||
|
||||
await bridge.connect(bridgeTransport);
|
||||
await app.connect(appTransport);
|
||||
|
||||
bridge.sendPromptListChanged();
|
||||
await flush();
|
||||
|
||||
expect(receivedNotifications).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,28 +1,36 @@
|
|||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import * as z from "zod/v4/core";
|
||||
|
||||
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
||||
import {
|
||||
CallToolRequest,
|
||||
CallToolRequestSchema,
|
||||
CallToolResult,
|
||||
CallToolResultSchema,
|
||||
Implementation,
|
||||
ListPromptsRequest,
|
||||
ListPromptsRequestSchema,
|
||||
ListPromptsResult,
|
||||
ListPromptsResultSchema,
|
||||
ListResourcesRequest,
|
||||
ListResourcesRequestSchema,
|
||||
ListResourcesResult,
|
||||
ListResourcesResultSchema,
|
||||
ListResourceTemplatesRequest,
|
||||
ListResourceTemplatesRequestSchema,
|
||||
ListResourceTemplatesResult,
|
||||
ListResourceTemplatesResultSchema,
|
||||
LoggingMessageNotification,
|
||||
LoggingMessageNotificationSchema,
|
||||
Notification,
|
||||
PingRequest,
|
||||
PingRequestSchema,
|
||||
PromptListChangedNotification,
|
||||
PromptListChangedNotificationSchema,
|
||||
ReadResourceRequest,
|
||||
ReadResourceRequestSchema,
|
||||
ReadResourceResult,
|
||||
ReadResourceResultSchema,
|
||||
Request,
|
||||
ResourceListChangedNotification,
|
||||
ResourceListChangedNotificationSchema,
|
||||
Result,
|
||||
ToolListChangedNotification,
|
||||
ToolListChangedNotificationSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import {
|
||||
|
|
@ -32,6 +40,9 @@ import {
|
|||
} from "@modelcontextprotocol/sdk/shared/protocol.js";
|
||||
|
||||
import {
|
||||
type AppNotification,
|
||||
type AppRequest,
|
||||
type AppResult,
|
||||
type McpUiSandboxResourceReadyNotification,
|
||||
type McpUiSizeChangedNotification,
|
||||
type McpUiToolCancelledNotification,
|
||||
|
|
@ -55,6 +66,7 @@ import {
|
|||
McpUiOpenLinkRequestSchema,
|
||||
McpUiOpenLinkResult,
|
||||
McpUiResourceTeardownRequest,
|
||||
McpUiResourceTeardownResult,
|
||||
McpUiResourceTeardownResultSchema,
|
||||
McpUiSandboxProxyReadyNotification,
|
||||
McpUiSandboxProxyReadyNotificationSchema,
|
||||
|
|
@ -153,7 +165,11 @@ type RequestHandlerExtra = Parameters<
|
|||
* await bridge.connect(transport);
|
||||
* ```
|
||||
*/
|
||||
export class AppBridge extends Protocol<Request, Notification, Result> {
|
||||
export class AppBridge extends Protocol<
|
||||
AppRequest,
|
||||
AppNotification,
|
||||
AppResult
|
||||
> {
|
||||
private _appCapabilities?: McpUiAppCapabilities;
|
||||
private _hostContext: McpUiHostContext = {};
|
||||
private _appInfo?: Implementation;
|
||||
|
|
@ -161,12 +177,15 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
/**
|
||||
* Create a new AppBridge instance.
|
||||
*
|
||||
* @param _client - MCP client connected to the server (for proxying requests)
|
||||
* @param _client - MCP client connected to the server, or `null`. When provided,
|
||||
* {@link connect} will automatically set up forwarding of MCP requests/notifications
|
||||
* between the Guest UI and the server. When `null`, you must register handlers
|
||||
* manually using the `oncalltool`, `onlistresources`, etc. setters.
|
||||
* @param _hostInfo - Host application identification (name and version)
|
||||
* @param _capabilities - Features and capabilities the host supports
|
||||
* @param options - Configuration options (inherited from Protocol)
|
||||
*
|
||||
* @example
|
||||
* @example With MCP client (automatic forwarding)
|
||||
* ```typescript
|
||||
* const bridge = new AppBridge(
|
||||
* mcpClient,
|
||||
|
|
@ -174,9 +193,19 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* { openLinks: {}, serverTools: {}, logging: {} }
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @example Without MCP client (manual handlers)
|
||||
* ```typescript
|
||||
* const bridge = new AppBridge(
|
||||
* null,
|
||||
* { name: "MyHost", version: "1.0.0" },
|
||||
* { openLinks: {}, serverTools: {}, logging: {} }
|
||||
* );
|
||||
* bridge.oncalltool = async (params, extra) => { ... };
|
||||
* ```
|
||||
*/
|
||||
constructor(
|
||||
private _client: Client,
|
||||
private _client: Client | null,
|
||||
private _hostInfo: Implementation,
|
||||
private _capabilities: McpUiHostCapabilities,
|
||||
options?: HostOptions,
|
||||
|
|
@ -503,11 +532,285 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for tool call requests from the Guest UI.
|
||||
*
|
||||
* The Guest UI sends `tools/call` requests to execute MCP server tools. This
|
||||
* handler allows the host to intercept and process these requests, typically
|
||||
* by forwarding them to the MCP server.
|
||||
*
|
||||
* @param callback - Handler that receives tool call params and returns a
|
||||
* {@link CallToolResult}
|
||||
* @param callback.params - Tool call parameters (name and arguments)
|
||||
* @param callback.extra - Request metadata (abort signal, session info)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* bridge.oncalltool = async ({ name, arguments: args }, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "tools/call", params: { name, arguments: args } },
|
||||
* CallToolResultSchema,
|
||||
* { signal: extra.signal }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link CallToolRequest} for the request type
|
||||
* @see {@link CallToolResult} for the result type
|
||||
*/
|
||||
set oncalltool(
|
||||
callback: (
|
||||
params: CallToolRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<CallToolResult>,
|
||||
) {
|
||||
this.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
||||
return callback(request.params, extra);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Guest UI that the MCP server's tool list has changed.
|
||||
*
|
||||
* The host sends `notifications/tools/list_changed` to the Guest UI when it
|
||||
* receives this notification from the MCP server. This allows the Guest UI
|
||||
* to refresh its tool cache or UI accordingly.
|
||||
*
|
||||
* @param params - Optional notification params (typically empty)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In your MCP client notification handler:
|
||||
* mcpClient.setNotificationHandler(ToolListChangedNotificationSchema, () => {
|
||||
* bridge.sendToolListChanged();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see {@link ToolListChangedNotification} for the notification type
|
||||
*/
|
||||
sendToolListChanged(params: ToolListChangedNotification["params"] = {}) {
|
||||
return this.notification({
|
||||
method: "notifications/tools/list_changed" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for list resources requests from the Guest UI.
|
||||
*
|
||||
* The Guest UI sends `resources/list` requests to enumerate available MCP
|
||||
* resources. This handler allows the host to intercept and process these
|
||||
* requests, typically by forwarding them to the MCP server.
|
||||
*
|
||||
* @param callback - Handler that receives list params and returns a
|
||||
* {@link ListResourcesResult}
|
||||
* @param callback.params - Request params (may include cursor for pagination)
|
||||
* @param callback.extra - Request metadata (abort signal, session info)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* bridge.onlistresources = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "resources/list", params },
|
||||
* ListResourcesResultSchema,
|
||||
* { signal: extra.signal }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link ListResourcesRequest} for the request type
|
||||
* @see {@link ListResourcesResult} for the result type
|
||||
*/
|
||||
set onlistresources(
|
||||
callback: (
|
||||
params: ListResourcesRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<ListResourcesResult>,
|
||||
) {
|
||||
this.setRequestHandler(
|
||||
ListResourcesRequestSchema,
|
||||
async (request, extra) => {
|
||||
return callback(request.params, extra);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for list resource templates requests from the Guest UI.
|
||||
*
|
||||
* The Guest UI sends `resources/templates/list` requests to enumerate available
|
||||
* MCP resource templates. This handler allows the host to intercept and process
|
||||
* these requests, typically by forwarding them to the MCP server.
|
||||
*
|
||||
* @param callback - Handler that receives list params and returns a
|
||||
* {@link ListResourceTemplatesResult}
|
||||
* @param callback.params - Request params (may include cursor for pagination)
|
||||
* @param callback.extra - Request metadata (abort signal, session info)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* bridge.onlistresourcetemplates = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "resources/templates/list", params },
|
||||
* ListResourceTemplatesResultSchema,
|
||||
* { signal: extra.signal }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link ListResourceTemplatesRequest} for the request type
|
||||
* @see {@link ListResourceTemplatesResult} for the result type
|
||||
*/
|
||||
set onlistresourcetemplates(
|
||||
callback: (
|
||||
params: ListResourceTemplatesRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<ListResourceTemplatesResult>,
|
||||
) {
|
||||
this.setRequestHandler(
|
||||
ListResourceTemplatesRequestSchema,
|
||||
async (request, extra) => {
|
||||
return callback(request.params, extra);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for read resource requests from the Guest UI.
|
||||
*
|
||||
* The Guest UI sends `resources/read` requests to retrieve the contents of an
|
||||
* MCP resource. This handler allows the host to intercept and process these
|
||||
* requests, typically by forwarding them to the MCP server.
|
||||
*
|
||||
* @param callback - Handler that receives read params and returns a
|
||||
* {@link ReadResourceResult}
|
||||
* @param callback.params - Read parameters including the resource URI
|
||||
* @param callback.extra - Request metadata (abort signal, session info)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* bridge.onreadresource = async ({ uri }, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "resources/read", params: { uri } },
|
||||
* ReadResourceResultSchema,
|
||||
* { signal: extra.signal }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link ReadResourceRequest} for the request type
|
||||
* @see {@link ReadResourceResult} for the result type
|
||||
*/
|
||||
set onreadresource(
|
||||
callback: (
|
||||
params: ReadResourceRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<ReadResourceResult>,
|
||||
) {
|
||||
this.setRequestHandler(
|
||||
ReadResourceRequestSchema,
|
||||
async (request, extra) => {
|
||||
return callback(request.params, extra);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Guest UI that the MCP server's resource list has changed.
|
||||
*
|
||||
* The host sends `notifications/resources/list_changed` to the Guest UI when it
|
||||
* receives this notification from the MCP server. This allows the Guest UI
|
||||
* to refresh its resource cache or UI accordingly.
|
||||
*
|
||||
* @param params - Optional notification params (typically empty)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In your MCP client notification handler:
|
||||
* mcpClient.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
|
||||
* bridge.sendResourceListChanged();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see {@link ResourceListChangedNotification} for the notification type
|
||||
*/
|
||||
sendResourceListChanged(
|
||||
params: ResourceListChangedNotification["params"] = {},
|
||||
) {
|
||||
return this.notification({
|
||||
method: "notifications/resources/list_changed" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler for list prompts requests from the Guest UI.
|
||||
*
|
||||
* The Guest UI sends `prompts/list` requests to enumerate available MCP
|
||||
* prompts. This handler allows the host to intercept and process these
|
||||
* requests, typically by forwarding them to the MCP server.
|
||||
*
|
||||
* @param callback - Handler that receives list params and returns a
|
||||
* {@link ListPromptsResult}
|
||||
* @param callback.params - Request params (may include cursor for pagination)
|
||||
* @param callback.extra - Request metadata (abort signal, session info)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* bridge.onlistprompts = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "prompts/list", params },
|
||||
* ListPromptsResultSchema,
|
||||
* { signal: extra.signal }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see {@link ListPromptsRequest} for the request type
|
||||
* @see {@link ListPromptsResult} for the result type
|
||||
*/
|
||||
set onlistprompts(
|
||||
callback: (
|
||||
params: ListPromptsRequest["params"],
|
||||
extra: RequestHandlerExtra,
|
||||
) => Promise<ListPromptsResult>,
|
||||
) {
|
||||
this.setRequestHandler(ListPromptsRequestSchema, async (request, extra) => {
|
||||
return callback(request.params, extra);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Guest UI that the MCP server's prompt list has changed.
|
||||
*
|
||||
* The host sends `notifications/prompts/list_changed` to the Guest UI when it
|
||||
* receives this notification from the MCP server. This allows the Guest UI
|
||||
* to refresh its prompt cache or UI accordingly.
|
||||
*
|
||||
* @param params - Optional notification params (typically empty)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In your MCP client notification handler:
|
||||
* mcpClient.setNotificationHandler(PromptListChangedNotificationSchema, () => {
|
||||
* bridge.sendPromptListChanged();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see {@link PromptListChangedNotification} for the notification type
|
||||
*/
|
||||
sendPromptListChanged(params: PromptListChangedNotification["params"] = {}) {
|
||||
return this.notification({
|
||||
method: "notifications/prompts/list_changed" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the guest supports the capability required for the given request method.
|
||||
* @internal
|
||||
*/
|
||||
assertCapabilityForMethod(method: Request["method"]): void {
|
||||
assertCapabilityForMethod(method: AppRequest["method"]): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
|
@ -515,7 +818,7 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* Verify that a request handler is registered and supported for the given method.
|
||||
* @internal
|
||||
*/
|
||||
assertRequestHandlerCapability(method: Request["method"]): void {
|
||||
assertRequestHandlerCapability(method: AppRequest["method"]): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
|
@ -523,7 +826,7 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* Verify that the host supports the capability required for the given notification method.
|
||||
* @internal
|
||||
*/
|
||||
assertNotificationCapability(method: Notification["method"]): void {
|
||||
assertNotificationCapability(method: AppNotification["method"]): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
|
@ -638,10 +941,10 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
sendHostContextChange(
|
||||
params: McpUiHostContextChangedNotification["params"],
|
||||
): Promise<void> | void {
|
||||
return this.notification((<McpUiHostContextChangedNotification>{
|
||||
method: "ui/notifications/host-context-changed",
|
||||
return this.notification({
|
||||
method: "ui/notifications/host-context-changed" as const,
|
||||
params,
|
||||
}) as Notification);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -667,8 +970,8 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* @see {@link sendToolResult} for sending results after execution
|
||||
*/
|
||||
sendToolInput(params: McpUiToolInputNotification["params"]) {
|
||||
return this.notification(<McpUiToolInputNotification>{
|
||||
method: "ui/notifications/tool-input",
|
||||
return this.notification({
|
||||
method: "ui/notifications/tool-input" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -701,8 +1004,8 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* @see {@link sendToolInput} for sending complete arguments
|
||||
*/
|
||||
sendToolInputPartial(params: McpUiToolInputPartialNotification["params"]) {
|
||||
return this.notification(<McpUiToolInputPartialNotification>{
|
||||
method: "ui/notifications/tool-input-partial",
|
||||
return this.notification({
|
||||
method: "ui/notifications/tool-input-partial" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -732,8 +1035,8 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* @see {@link sendToolInput} for sending tool arguments before results
|
||||
*/
|
||||
sendToolResult(params: McpUiToolResultNotification["params"]) {
|
||||
return this.notification(<McpUiToolResultNotification>{
|
||||
method: "ui/notifications/tool-result",
|
||||
return this.notification({
|
||||
method: "ui/notifications/tool-result" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -769,8 +1072,8 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* @see {@link sendToolInput} for sending tool arguments
|
||||
*/
|
||||
sendToolCancelled(params: McpUiToolCancelledNotification["params"]) {
|
||||
return this.notification(<McpUiToolCancelledNotification>{
|
||||
method: "ui/notifications/tool-cancelled",
|
||||
return this.notification({
|
||||
method: "ui/notifications/tool-cancelled" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -793,8 +1096,8 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
sendSandboxResourceReady(
|
||||
params: McpUiSandboxResourceReadyNotification["params"],
|
||||
) {
|
||||
return this.notification(<McpUiSandboxResourceReadyNotification>{
|
||||
method: "ui/notifications/sandbox-resource-ready",
|
||||
return this.notification({
|
||||
method: "ui/notifications/sandbox-resource-ready" as const,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
|
@ -828,8 +1131,8 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
options?: RequestOptions,
|
||||
) {
|
||||
return this.request(
|
||||
<McpUiResourceTeardownRequest>{
|
||||
method: "ui/resource-teardown",
|
||||
{
|
||||
method: "ui/resource-teardown" as const,
|
||||
params,
|
||||
},
|
||||
McpUiResourceTeardownResultSchema,
|
||||
|
|
@ -837,39 +1140,19 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
);
|
||||
}
|
||||
|
||||
private forwardRequest<
|
||||
Req extends z.$ZodObject<{
|
||||
method: z.$ZodLiteral<string>;
|
||||
}>,
|
||||
Res extends z.$ZodObject<{}>,
|
||||
>(requestSchema: Req, resultSchema: Res) {
|
||||
this.setRequestHandler(requestSchema, async (request, extra) => {
|
||||
console.log(`Forwarding request ${request.method} from MCP UI client`);
|
||||
return this._client.request(request, resultSchema, {
|
||||
signal: extra.signal,
|
||||
});
|
||||
});
|
||||
}
|
||||
private forwardNotification<
|
||||
N extends z.$ZodObject<{ method: z.$ZodLiteral<string> }>,
|
||||
>(notificationSchema: N) {
|
||||
this.setNotificationHandler(notificationSchema, async (notification) => {
|
||||
console.log(
|
||||
`Forwarding notification ${notification.method} from MCP UI client`,
|
||||
);
|
||||
await this._client.notification(notification);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the Guest UI via transport and set up message forwarding.
|
||||
* Connect to the Guest UI via transport and optionally set up message forwarding.
|
||||
*
|
||||
* This method establishes the transport connection and automatically sets up
|
||||
* request/notification forwarding based on the MCP server's capabilities.
|
||||
* It proxies the following server capabilities to the Guest UI:
|
||||
* - Tools (tools/call, tools/list_changed)
|
||||
* - Resources (resources/list, resources/read, resources/templates/list, resources/list_changed)
|
||||
* - Prompts (prompts/list, prompts/list_changed)
|
||||
* This method establishes the transport connection. If an MCP client was passed
|
||||
* to the constructor, it also automatically sets up request/notification forwarding
|
||||
* based on the MCP server's capabilities, proxying the following to the Guest UI:
|
||||
* - Tools (tools/call, notifications/tools/list_changed)
|
||||
* - Resources (resources/list, resources/read, resources/templates/list, notifications/resources/list_changed)
|
||||
* - Prompts (prompts/list, notifications/prompts/list_changed)
|
||||
*
|
||||
* If no client was passed to the constructor, no automatic forwarding is set up
|
||||
* and you must register handlers manually using the `oncalltool`, `onlistresources`,
|
||||
* etc. setters.
|
||||
*
|
||||
* After calling connect, wait for the `oninitialized` callback before sending
|
||||
* tool input and other data to the Guest UI.
|
||||
|
|
@ -877,12 +1160,12 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
* @param transport - Transport layer (typically PostMessageTransport)
|
||||
* @returns Promise resolving when connection is established
|
||||
*
|
||||
* @throws {Error} If server capabilities are not available. This occurs when
|
||||
* connect() is called before the MCP client has completed its initialization
|
||||
* with the server. Ensure `await client.connect()` completes before calling
|
||||
* `bridge.connect()`.
|
||||
* @throws {Error} If a client was passed but server capabilities are not available.
|
||||
* This occurs when connect() is called before the MCP client has completed its
|
||||
* initialization with the server. Ensure `await client.connect()` completes
|
||||
* before calling `bridge.connect()`.
|
||||
*
|
||||
* @example
|
||||
* @example With MCP client (automatic forwarding)
|
||||
* ```typescript
|
||||
* const bridge = new AppBridge(mcpClient, hostInfo, capabilities);
|
||||
* const transport = new PostMessageTransport(
|
||||
|
|
@ -897,38 +1180,86 @@ export class AppBridge extends Protocol<Request, Notification, Result> {
|
|||
*
|
||||
* await bridge.connect(transport);
|
||||
* ```
|
||||
*
|
||||
* @example Without MCP client (manual handlers)
|
||||
* ```typescript
|
||||
* const bridge = new AppBridge(null, hostInfo, capabilities);
|
||||
*
|
||||
* // Register handlers manually
|
||||
* bridge.oncalltool = async (params, extra) => {
|
||||
* // Custom tool call handling
|
||||
* };
|
||||
*
|
||||
* await bridge.connect(transport);
|
||||
* ```
|
||||
*/
|
||||
async connect(transport: Transport) {
|
||||
// Forward core available MCP features
|
||||
const serverCapabilities = this._client.getServerCapabilities();
|
||||
if (!serverCapabilities) {
|
||||
throw new Error("Client server capabilities not available");
|
||||
}
|
||||
if (this._client) {
|
||||
// When a client was passed to the constructor, automatically forward
|
||||
// MCP requests/notifications between the Guest UI and the server
|
||||
const serverCapabilities = this._client.getServerCapabilities();
|
||||
if (!serverCapabilities) {
|
||||
throw new Error("Client server capabilities not available");
|
||||
}
|
||||
|
||||
if (serverCapabilities.tools) {
|
||||
this.forwardRequest(CallToolRequestSchema, CallToolResultSchema);
|
||||
if (serverCapabilities.tools.listChanged) {
|
||||
this.forwardNotification(ToolListChangedNotificationSchema);
|
||||
if (serverCapabilities.tools) {
|
||||
this.oncalltool = async (params, extra) => {
|
||||
return this._client!.request(
|
||||
{ method: "tools/call", params },
|
||||
CallToolResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
if (serverCapabilities.tools.listChanged) {
|
||||
this._client.setNotificationHandler(
|
||||
ToolListChangedNotificationSchema,
|
||||
(n) => this.sendToolListChanged(n.params),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (serverCapabilities.resources) {
|
||||
this.forwardRequest(
|
||||
ListResourcesRequestSchema,
|
||||
ListResourcesResultSchema,
|
||||
);
|
||||
this.forwardRequest(
|
||||
ListResourceTemplatesRequestSchema,
|
||||
ListResourceTemplatesResultSchema,
|
||||
);
|
||||
this.forwardRequest(ReadResourceRequestSchema, ReadResourceResultSchema);
|
||||
if (serverCapabilities.resources.listChanged) {
|
||||
this.forwardNotification(ResourceListChangedNotificationSchema);
|
||||
if (serverCapabilities.resources) {
|
||||
this.onlistresources = async (params, extra) => {
|
||||
return this._client!.request(
|
||||
{ method: "resources/list", params },
|
||||
ListResourcesResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
this.onlistresourcetemplates = async (params, extra) => {
|
||||
return this._client!.request(
|
||||
{ method: "resources/templates/list", params },
|
||||
ListResourceTemplatesResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
this.onreadresource = async (params, extra) => {
|
||||
return this._client!.request(
|
||||
{ method: "resources/read", params },
|
||||
ReadResourceResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
if (serverCapabilities.resources.listChanged) {
|
||||
this._client.setNotificationHandler(
|
||||
ResourceListChangedNotificationSchema,
|
||||
(n) => this.sendResourceListChanged(n.params),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (serverCapabilities.prompts) {
|
||||
this.forwardRequest(ListPromptsRequestSchema, ListPromptsResultSchema);
|
||||
if (serverCapabilities.prompts.listChanged) {
|
||||
this.forwardNotification(PromptListChangedNotificationSchema);
|
||||
if (serverCapabilities.prompts) {
|
||||
this.onlistprompts = async (params, extra) => {
|
||||
return this._client!.request(
|
||||
{ method: "prompts/list", params },
|
||||
ListPromptsResultSchema,
|
||||
{ signal: extra.signal },
|
||||
);
|
||||
};
|
||||
if (serverCapabilities.prompts.listChanged) {
|
||||
this._client.setNotificationHandler(
|
||||
PromptListChangedNotificationSchema,
|
||||
(n) => this.sendPromptListChanged(n.params),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/app.ts
12
src/app.ts
|
|
@ -13,11 +13,9 @@ import {
|
|||
ListToolsRequest,
|
||||
ListToolsRequestSchema,
|
||||
LoggingMessageNotification,
|
||||
Notification,
|
||||
PingRequestSchema,
|
||||
Request,
|
||||
Result,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { AppNotification, AppRequest, AppResult } from "./types";
|
||||
import {
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
McpUiAppCapabilities,
|
||||
|
|
@ -189,7 +187,7 @@ type RequestHandlerExtra = Parameters<
|
|||
* });
|
||||
* ```
|
||||
*/
|
||||
export class App extends Protocol<Request, Notification, Result> {
|
||||
export class App extends Protocol<AppRequest, AppNotification, AppResult> {
|
||||
private _hostCapabilities?: McpUiHostCapabilities;
|
||||
private _hostInfo?: Implementation;
|
||||
private _hostContext?: McpUiHostContext;
|
||||
|
|
@ -642,7 +640,7 @@ export class App extends Protocol<Request, Notification, Result> {
|
|||
* Verify that the host supports the capability required for the given request method.
|
||||
* @internal
|
||||
*/
|
||||
assertCapabilityForMethod(method: Request["method"]): void {
|
||||
assertCapabilityForMethod(method: AppRequest["method"]): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
|
@ -650,7 +648,7 @@ export class App extends Protocol<Request, Notification, Result> {
|
|||
* Verify that the app declared the capability required for the given request method.
|
||||
* @internal
|
||||
*/
|
||||
assertRequestHandlerCapability(method: Request["method"]): void {
|
||||
assertRequestHandlerCapability(method: AppRequest["method"]): void {
|
||||
switch (method) {
|
||||
case "tools/call":
|
||||
case "tools/list":
|
||||
|
|
@ -672,7 +670,7 @@ export class App extends Protocol<Request, Notification, Result> {
|
|||
* Verify that the app supports the capability required for the given notification method.
|
||||
* @internal
|
||||
*/
|
||||
assertNotificationCapability(method: Notification["method"]): void {
|
||||
assertNotificationCapability(method: AppNotification["method"]): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@
|
|||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": ["method", "params"],
|
||||
|
|
@ -667,7 +667,7 @@
|
|||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"McpUiInitializeRequest": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
|
|
@ -1135,7 +1135,7 @@
|
|||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"additionalProperties": {}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ export const McpUiToolResultNotificationSchema = z.object({
|
|||
/**
|
||||
* @description Rich context about the host environment provided to Guest UIs.
|
||||
*/
|
||||
export const McpUiHostContextSchema = z.object({
|
||||
export const McpUiHostContextSchema = z.looseObject({
|
||||
/** @description Metadata of the tool call that instantiated this App. */
|
||||
toolInfo: z
|
||||
.object({
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ export interface McpUiToolCancelledNotification {
|
|||
* @description Rich context about the host environment provided to Guest UIs.
|
||||
*/
|
||||
export interface McpUiHostContext {
|
||||
/** @description Allow additional properties for forward compatibility. */
|
||||
[key: string]: unknown;
|
||||
/** @description Metadata of the tool call that instantiated this App. */
|
||||
toolInfo?: {
|
||||
/** @description JSON-RPC id of the tools/call request. */
|
||||
|
|
|
|||
110
src/types.ts
110
src/types.ts
|
|
@ -38,6 +38,27 @@ export {
|
|||
type McpUiResourceMeta,
|
||||
} from "./spec.types.js";
|
||||
|
||||
// Import types needed for protocol type unions (not re-exported, just used internally)
|
||||
import type {
|
||||
McpUiInitializeRequest,
|
||||
McpUiOpenLinkRequest,
|
||||
McpUiMessageRequest,
|
||||
McpUiResourceTeardownRequest,
|
||||
McpUiHostContextChangedNotification,
|
||||
McpUiToolInputNotification,
|
||||
McpUiToolInputPartialNotification,
|
||||
McpUiToolResultNotification,
|
||||
McpUiToolCancelledNotification,
|
||||
McpUiSandboxResourceReadyNotification,
|
||||
McpUiInitializedNotification,
|
||||
McpUiSizeChangedNotification,
|
||||
McpUiSandboxProxyReadyNotification,
|
||||
McpUiInitializeResult,
|
||||
McpUiOpenLinkResult,
|
||||
McpUiMessageResult,
|
||||
McpUiResourceTeardownResult,
|
||||
} from "./spec.types.js";
|
||||
|
||||
// Re-export all schemas from generated/schema.ts (already PascalCase)
|
||||
export {
|
||||
McpUiThemeSchema,
|
||||
|
|
@ -65,3 +86,92 @@ export {
|
|||
McpUiResourceCspSchema,
|
||||
McpUiResourceMetaSchema,
|
||||
} from "./generated/schema.js";
|
||||
|
||||
// Re-export SDK types used in protocol type unions
|
||||
import {
|
||||
CallToolRequest,
|
||||
CallToolResult,
|
||||
EmptyResult,
|
||||
ListPromptsRequest,
|
||||
ListPromptsResult,
|
||||
ListResourcesRequest,
|
||||
ListResourcesResult,
|
||||
ListResourceTemplatesRequest,
|
||||
ListResourceTemplatesResult,
|
||||
ListToolsRequest,
|
||||
ListToolsResult,
|
||||
LoggingMessageNotification,
|
||||
PingRequest,
|
||||
PromptListChangedNotification,
|
||||
ReadResourceRequest,
|
||||
ReadResourceResult,
|
||||
ResourceListChangedNotification,
|
||||
ToolListChangedNotification,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
|
||||
/**
|
||||
* All request types in the MCP Apps protocol.
|
||||
*
|
||||
* Includes:
|
||||
* - MCP UI requests (initialize, open-link, message, resource-teardown)
|
||||
* - MCP server requests forwarded from the app (tools/call, resources/*, prompts/list)
|
||||
* - Protocol requests (ping)
|
||||
*/
|
||||
export type AppRequest =
|
||||
| McpUiInitializeRequest
|
||||
| McpUiOpenLinkRequest
|
||||
| McpUiMessageRequest
|
||||
| McpUiResourceTeardownRequest
|
||||
| CallToolRequest
|
||||
| ListToolsRequest
|
||||
| ListResourcesRequest
|
||||
| ListResourceTemplatesRequest
|
||||
| ReadResourceRequest
|
||||
| ListPromptsRequest
|
||||
| PingRequest;
|
||||
|
||||
/**
|
||||
* All notification types in the MCP Apps protocol.
|
||||
*
|
||||
* Host to app:
|
||||
* - Tool lifecycle (input, input-partial, result, cancelled)
|
||||
* - Host context changes
|
||||
* - MCP list changes (tools, resources, prompts)
|
||||
* - Sandbox resource ready
|
||||
*
|
||||
* App to host:
|
||||
* - Initialized, size-changed, sandbox-proxy-ready
|
||||
* - Logging messages
|
||||
*/
|
||||
export type AppNotification =
|
||||
// Sent to app
|
||||
| McpUiHostContextChangedNotification
|
||||
| McpUiToolInputNotification
|
||||
| McpUiToolInputPartialNotification
|
||||
| McpUiToolResultNotification
|
||||
| McpUiToolCancelledNotification
|
||||
| McpUiSandboxResourceReadyNotification
|
||||
| ToolListChangedNotification
|
||||
| ResourceListChangedNotification
|
||||
| PromptListChangedNotification
|
||||
// Received from app
|
||||
| McpUiInitializedNotification
|
||||
| McpUiSizeChangedNotification
|
||||
| McpUiSandboxProxyReadyNotification
|
||||
| LoggingMessageNotification;
|
||||
|
||||
/**
|
||||
* All result types in the MCP Apps protocol.
|
||||
*/
|
||||
export type AppResult =
|
||||
| McpUiInitializeResult
|
||||
| McpUiOpenLinkResult
|
||||
| McpUiMessageResult
|
||||
| McpUiResourceTeardownResult
|
||||
| CallToolResult
|
||||
| ListToolsResult
|
||||
| ListResourcesResult
|
||||
| ListResourceTemplatesResult
|
||||
| ReadResourceResult
|
||||
| ListPromptsResult
|
||||
| EmptyResult;
|
||||
|
|
|
|||
Loading…
Reference in a new issue