refactor: finish MCP SDK v2 migration
This commit is contained in:
parent
a76e3a7691
commit
f94cb38b0b
9 changed files with 684 additions and 540 deletions
|
|
@ -7,17 +7,11 @@
|
|||
* @module
|
||||
*/
|
||||
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
||||
import {
|
||||
CallToolResult,
|
||||
CallToolResultSchema,
|
||||
import { Client, type Transport } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
CreateMessageRequest,
|
||||
CreateMessageResult,
|
||||
ListResourcesResultSchema,
|
||||
ReadResourceResultSchema,
|
||||
ListPromptsResultSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
} from "@modelcontextprotocol/server";
|
||||
import { AppBridge, PostMessageTransport } from "./app-bridge.js";
|
||||
import type { McpUiDisplayMode } from "./types.js";
|
||||
|
||||
|
|
@ -223,8 +217,7 @@ function AppBridge_oncalltool_forwardToServer(
|
|||
bridge.oncalltool = async (params, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "tools/call", params },
|
||||
CallToolResultSchema,
|
||||
{ signal: extra.signal },
|
||||
{ signal: extra.mcpReq.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_oncalltool_forwardToServer
|
||||
|
|
@ -245,7 +238,9 @@ function AppBridge_oncreatesamplingmessage_forwardToLlm(
|
|||
//#region AppBridge_oncreatesamplingmessage_forwardToLlm
|
||||
bridge.oncreatesamplingmessage = async (params, extra) => {
|
||||
// Apply rate limiting, user approval, cost controls here
|
||||
return await myLlmProvider.complete(params, { signal: extra.signal });
|
||||
return await myLlmProvider.complete(params, {
|
||||
signal: extra.mcpReq.signal,
|
||||
});
|
||||
};
|
||||
//#endregion AppBridge_oncreatesamplingmessage_forwardToLlm
|
||||
}
|
||||
|
|
@ -261,8 +256,7 @@ function AppBridge_onlistresources_returnResources(
|
|||
bridge.onlistresources = async (params, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "resources/list", params },
|
||||
ListResourcesResultSchema,
|
||||
{ signal: extra.signal },
|
||||
{ signal: extra.mcpReq.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_onlistresources_returnResources
|
||||
|
|
@ -279,8 +273,7 @@ function AppBridge_onreadresource_returnResource(
|
|||
bridge.onreadresource = async (params, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "resources/read", params },
|
||||
ReadResourceResultSchema,
|
||||
{ signal: extra.signal },
|
||||
{ signal: extra.mcpReq.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_onreadresource_returnResource
|
||||
|
|
@ -297,8 +290,7 @@ function AppBridge_onlistprompts_returnPrompts(
|
|||
bridge.onlistprompts = async (params, extra) => {
|
||||
return mcpClient.request(
|
||||
{ method: "prompts/list", params },
|
||||
ListPromptsResultSchema,
|
||||
{ signal: extra.signal },
|
||||
{ signal: extra.mcpReq.signal },
|
||||
);
|
||||
};
|
||||
//#endregion AppBridge_onlistprompts_returnPrompts
|
||||
|
|
@ -444,10 +436,10 @@ async function AppBridge_sendToolResult_afterExecution(
|
|||
args: Record<string, unknown>,
|
||||
) {
|
||||
//#region AppBridge_sendToolResult_afterExecution
|
||||
const result = await mcpClient.request(
|
||||
{ method: "tools/call", params: { name: "get_weather", arguments: args } },
|
||||
CallToolResultSchema,
|
||||
);
|
||||
const result = await mcpClient.request({
|
||||
method: "tools/call",
|
||||
params: { name: "get_weather", arguments: args },
|
||||
});
|
||||
bridge.sendToolResult(result);
|
||||
//#endregion AppBridge_sendToolResult_afterExecution
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ export type AppBridgeEventMap = {
|
|||
/**
|
||||
* Host-side bridge for communicating with a single View ({@link app!App `App`}).
|
||||
*
|
||||
* `AppBridge` extends the MCP SDK's `Protocol` class and acts as a proxy between
|
||||
* `AppBridge` extends the base MCP SDK's public `Server` class and acts as a proxy between
|
||||
* the host application and a view running in an iframe. When an MCP client
|
||||
* is provided to the constructor, it automatically forwards MCP server capabilities
|
||||
* (tools, resources, prompts) to the view. It also handles the initialization
|
||||
|
|
@ -667,8 +667,7 @@ export class AppBridge extends Server {
|
|||
* ```typescript
|
||||
* bridge.onsandboxready = async () => {
|
||||
* const resource = await mcpClient.request(
|
||||
* { method: "resources/read", params: { uri: "ui://my-app" } },
|
||||
* ReadResourceResultSchema
|
||||
* { method: "resources/read", params: { uri: "ui://my-app" } }
|
||||
* );
|
||||
*
|
||||
* bridge.sendSandboxResourceReady({
|
||||
|
|
@ -1154,14 +1153,13 @@ export class AppBridge extends Server {
|
|||
* bridge.oncalltool = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "tools/call", params },
|
||||
* CallToolResultSchema,
|
||||
* { signal: extra.signal },
|
||||
* { signal: extra.mcpReq.signal },
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see `CallToolRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `CallToolResult` from @modelcontextprotocol/sdk for the result type
|
||||
* @see `CallToolRequest` from @modelcontextprotocol/server for the request type
|
||||
* @see `CallToolResult` from @modelcontextprotocol/server for the result type
|
||||
*/
|
||||
private _oncalltool?: (
|
||||
params: CallToolRequest["params"],
|
||||
|
|
@ -1209,12 +1207,14 @@ export class AppBridge extends Server {
|
|||
* ```ts source="./app-bridge.examples.ts#AppBridge_oncreatesamplingmessage_forwardToLlm"
|
||||
* bridge.oncreatesamplingmessage = async (params, extra) => {
|
||||
* // Apply rate limiting, user approval, cost controls here
|
||||
* return await myLlmProvider.complete(params, { signal: extra.signal });
|
||||
* return await myLlmProvider.complete(params, {
|
||||
* signal: extra.mcpReq.signal,
|
||||
* });
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see `CreateMessageRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/sdk for result types
|
||||
* @see `CreateMessageRequest` from @modelcontextprotocol/server for the request type
|
||||
* @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/server for result types
|
||||
*/
|
||||
set oncreatesamplingmessage(
|
||||
callback: (
|
||||
|
|
@ -1244,7 +1244,7 @@ export class AppBridge extends Server {
|
|||
* });
|
||||
* ```
|
||||
*
|
||||
* @see `ToolListChangedNotification` from @modelcontextprotocol/sdk for the notification type
|
||||
* @see `ToolListChangedNotification` from @modelcontextprotocol/server for the notification type
|
||||
*/
|
||||
sendToolListChanged(params: ToolListChangedNotification["params"] = {}) {
|
||||
return this.notification({
|
||||
|
|
@ -1270,14 +1270,13 @@ export class AppBridge extends Server {
|
|||
* bridge.onlistresources = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "resources/list", params },
|
||||
* ListResourcesResultSchema,
|
||||
* { signal: extra.signal },
|
||||
* { signal: extra.mcpReq.signal },
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see `ListResourcesRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `ListResourcesResult` from @modelcontextprotocol/sdk for the result type
|
||||
* @see `ListResourcesRequest` from @modelcontextprotocol/server for the request type
|
||||
* @see `ListResourcesResult` from @modelcontextprotocol/server for the result type
|
||||
*/
|
||||
private _onlistresources?: (
|
||||
params: ListResourcesRequest["params"],
|
||||
|
|
@ -1325,14 +1324,13 @@ export class AppBridge extends Server {
|
|||
* bridge.onlistresourcetemplates = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "resources/templates/list", params },
|
||||
* ListResourceTemplatesResultSchema,
|
||||
* { signal: extra.signal }
|
||||
* { signal: extra.mcpReq.signal }
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see `ListResourceTemplatesRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `ListResourceTemplatesResult` from @modelcontextprotocol/sdk for the result type
|
||||
* @see `ListResourceTemplatesRequest` from @modelcontextprotocol/server for the request type
|
||||
* @see `ListResourceTemplatesResult` from @modelcontextprotocol/server for the result type
|
||||
*/
|
||||
private _onlistresourcetemplates?: (
|
||||
params: ListResourceTemplatesRequest["params"],
|
||||
|
|
@ -1383,14 +1381,13 @@ export class AppBridge extends Server {
|
|||
* bridge.onreadresource = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "resources/read", params },
|
||||
* ReadResourceResultSchema,
|
||||
* { signal: extra.signal },
|
||||
* { signal: extra.mcpReq.signal },
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see `ReadResourceRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `ReadResourceResult` from @modelcontextprotocol/sdk for the result type
|
||||
* @see `ReadResourceRequest` from @modelcontextprotocol/server for the request type
|
||||
* @see `ReadResourceResult` from @modelcontextprotocol/server for the result type
|
||||
*/
|
||||
private _onreadresource?: (
|
||||
params: ReadResourceRequest["params"],
|
||||
|
|
@ -1438,7 +1435,7 @@ export class AppBridge extends Server {
|
|||
* });
|
||||
* ```
|
||||
*
|
||||
* @see `ResourceListChangedNotification` from @modelcontextprotocol/sdk for the notification type
|
||||
* @see `ResourceListChangedNotification` from @modelcontextprotocol/server for the notification type
|
||||
*/
|
||||
sendResourceListChanged(
|
||||
params: ResourceListChangedNotification["params"] = {},
|
||||
|
|
@ -1466,14 +1463,13 @@ export class AppBridge extends Server {
|
|||
* bridge.onlistprompts = async (params, extra) => {
|
||||
* return mcpClient.request(
|
||||
* { method: "prompts/list", params },
|
||||
* ListPromptsResultSchema,
|
||||
* { signal: extra.signal },
|
||||
* { signal: extra.mcpReq.signal },
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see `ListPromptsRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `ListPromptsResult` from @modelcontextprotocol/sdk for the result type
|
||||
* @see `ListPromptsRequest` from @modelcontextprotocol/server for the request type
|
||||
* @see `ListPromptsResult` from @modelcontextprotocol/server for the result type
|
||||
*/
|
||||
private _onlistprompts?: (
|
||||
params: ListPromptsRequest["params"],
|
||||
|
|
@ -1520,7 +1516,7 @@ export class AppBridge extends Server {
|
|||
* });
|
||||
* ```
|
||||
*
|
||||
* @see `PromptListChangedNotification` from @modelcontextprotocol/sdk for the notification type
|
||||
* @see `PromptListChangedNotification` from @modelcontextprotocol/server for the notification type
|
||||
*/
|
||||
sendPromptListChanged(params: PromptListChangedNotification["params"] = {}) {
|
||||
return this.notification({
|
||||
|
|
@ -1722,10 +1718,10 @@ export class AppBridge extends Server {
|
|||
*
|
||||
* @example
|
||||
* ```ts source="./app-bridge.examples.ts#AppBridge_sendToolResult_afterExecution"
|
||||
* const result = await mcpClient.request(
|
||||
* { method: "tools/call", params: { name: "get_weather", arguments: args } },
|
||||
* CallToolResultSchema,
|
||||
* );
|
||||
* const result = await mcpClient.request({
|
||||
* method: "tools/call",
|
||||
* params: { name: "get_weather", arguments: args },
|
||||
* });
|
||||
* bridge.sendToolResult(result);
|
||||
* ```
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@
|
|||
* @module
|
||||
*/
|
||||
|
||||
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||
import type {
|
||||
McpServer,
|
||||
ToolCallback,
|
||||
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import type { SamplingMessage, Tool } from "@modelcontextprotocol/client";
|
||||
import type { McpServer, ToolCallback } from "@modelcontextprotocol/server";
|
||||
import {
|
||||
App,
|
||||
PostMessageTransport,
|
||||
|
|
@ -369,7 +366,7 @@ async function App_createSamplingMessage_simple(app: App) {
|
|||
*/
|
||||
async function App_createSamplingMessage_withTools(
|
||||
app: App,
|
||||
messages: import("@modelcontextprotocol/sdk/types.js").SamplingMessage[],
|
||||
messages: SamplingMessage[],
|
||||
) {
|
||||
//#region App_createSamplingMessage_withTools
|
||||
if (!app.getHostCapabilities()?.sampling?.tools) return;
|
||||
|
|
|
|||
19
src/app.ts
19
src/app.ts
|
|
@ -174,9 +174,9 @@ export const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|||
/**
|
||||
* Options for configuring {@link App `App`} behavior.
|
||||
*
|
||||
* Extends `ProtocolOptions` from the MCP SDK with `App`-specific configuration.
|
||||
* Extends `ClientOptions` from the base MCP SDK with `App`-specific configuration.
|
||||
*
|
||||
* @see `ProtocolOptions` from @modelcontextprotocol/sdk for inherited options
|
||||
* @see `ClientOptions` from @modelcontextprotocol/client for inherited options
|
||||
*/
|
||||
export type AppOptions = Omit<
|
||||
ClientOptions,
|
||||
|
|
@ -243,7 +243,7 @@ export type AppToolResult<
|
|||
* validated/parsed input; when `In` is `undefined`, the callback receives only
|
||||
* `extra`. When `Out` is provided, the return's `structuredContent` is typed.
|
||||
*
|
||||
* Mirrors `ToolCallback` from `@modelcontextprotocol/sdk/server/mcp.js` but is
|
||||
* Mirrors `ToolCallback` from `@modelcontextprotocol/server` but is
|
||||
* parameterized over {@link StandardSchemaV1} instead of zod, so any
|
||||
* Standard-Schema-compatible library (Zod, ArkType, Valibot, …) can be used.
|
||||
*/
|
||||
|
|
@ -261,7 +261,7 @@ export type AppToolCallback<
|
|||
|
||||
/**
|
||||
* Handle returned by {@link App.registerTool}. Mirrors `RegisteredTool` from
|
||||
* `@modelcontextprotocol/sdk/server/mcp.js` but stores
|
||||
* `@modelcontextprotocol/server` but stores
|
||||
* {@link StandardSchemaV1} schemas.
|
||||
*/
|
||||
export type RegisteredAppTool = {
|
||||
|
|
@ -302,9 +302,8 @@ export type AppEventMap = {
|
|||
* Main class for MCP Apps to communicate with their host.
|
||||
*
|
||||
* The `App` class provides a framework-agnostic way to build interactive MCP Apps
|
||||
* that run inside host applications. It extends the MCP SDK's `Protocol` class and
|
||||
* handles the connection lifecycle, initialization handshake, and bidirectional
|
||||
* communication with the host.
|
||||
* that run inside host applications. It extends the base MCP SDK's public
|
||||
* `Client` class and adds the Apps handshake, lifecycle, and high-level APIs.
|
||||
*
|
||||
* ## Architecture
|
||||
*
|
||||
|
|
@ -321,7 +320,7 @@ export type AppEventMap = {
|
|||
*
|
||||
* ## Protocol and event methods
|
||||
*
|
||||
* As a subclass of the MCP SDK's `Protocol`, `App` provides:
|
||||
* As a subclass of the base MCP SDK's `Client`, `App` provides:
|
||||
* - `setRequestHandler()` - Register handlers for requests from host
|
||||
* - `setNotificationHandler()` - Register handlers for notifications from host
|
||||
* - `addEventListener()` - Append a listener for a notification event (multi-listener)
|
||||
|
|
@ -1458,8 +1457,8 @@ export class App extends Client {
|
|||
* }
|
||||
* ```
|
||||
*
|
||||
* @see `CreateMessageRequest` from @modelcontextprotocol/sdk for the request type
|
||||
* @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/sdk for result types
|
||||
* @see `CreateMessageRequest` from @modelcontextprotocol/client for the request type
|
||||
* @see `CreateMessageResult` / `CreateMessageResultWithTools` from @modelcontextprotocol/client for result types
|
||||
*/
|
||||
async createSamplingMessage(
|
||||
params: CreateMessageRequest["params"] & { tools?: undefined },
|
||||
|
|
|
|||
|
|
@ -38,9 +38,8 @@ export class EventDispatcher<EventMap extends Record<string, unknown>> {
|
|||
getHandler<K extends keyof EventMap>(
|
||||
event: K,
|
||||
): ((params: EventMap[K]) => void) | undefined {
|
||||
return (this._eventSlots.get(event) as
|
||||
| EventSlot<EventMap[K]>
|
||||
| undefined)?.onHandler;
|
||||
return (this._eventSlots.get(event) as EventSlot<EventMap[K]> | undefined)
|
||||
?.onHandler;
|
||||
}
|
||||
|
||||
/** Add a listener. Listeners run in insertion order after the handler. */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test";
|
||||
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
||||
import type { JSONRPCMessage } from "@modelcontextprotocol/client";
|
||||
|
||||
import { PostMessageTransport } from "./message-transport";
|
||||
|
||||
|
|
|
|||
|
|
@ -9,17 +9,14 @@
|
|||
|
||||
import * as crypto from "node:crypto";
|
||||
import * as fs from "node:fs/promises";
|
||||
import type {
|
||||
McpServer,
|
||||
ToolCallback,
|
||||
ReadResourceCallback,
|
||||
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import type { McpServer, ToolCallback } from "@modelcontextprotocol/server";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
registerAppTool,
|
||||
registerAppResource,
|
||||
getUiCapability,
|
||||
RESOURCE_MIME_TYPE,
|
||||
type ReadResourceCallback,
|
||||
} from "./index.js";
|
||||
|
||||
// Stubs for external functions used in examples
|
||||
|
|
@ -74,7 +71,7 @@ function registerAppTool_basicUsage(server: McpServer) {
|
|||
{
|
||||
title: "Get Weather",
|
||||
description: "Get current weather for a location",
|
||||
inputSchema: { location: z.string() },
|
||||
inputSchema: z.object({ location: z.string() }),
|
||||
_meta: {
|
||||
ui: { resourceUri: "ui://weather/view.html" },
|
||||
},
|
||||
|
|
@ -122,7 +119,7 @@ function registerAppTool_appOnlyVisibility(server: McpServer) {
|
|||
"update-quantity",
|
||||
{
|
||||
description: "Update item quantity in cart",
|
||||
inputSchema: { itemId: z.string(), quantity: z.number() },
|
||||
inputSchema: z.object({ itemId: z.string(), quantity: z.number() }),
|
||||
_meta: {
|
||||
ui: {
|
||||
resourceUri: "ui://shop/cart.html",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
getUiCapability,
|
||||
EXTENSION_ID,
|
||||
} from "./index";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/server";
|
||||
|
||||
describe("registerAppTool", () => {
|
||||
it("should pass through config to server.registerTool", () => {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export interface McpUiAppResourceConfig extends ResourceMetadata {
|
|||
* {
|
||||
* title: "Get Weather",
|
||||
* description: "Get current weather for a location",
|
||||
* inputSchema: { location: z.string() },
|
||||
* inputSchema: z.object({ location: z.string() }),
|
||||
* _meta: {
|
||||
* ui: { resourceUri: "ui://weather/view.html" },
|
||||
* },
|
||||
|
|
@ -190,7 +190,7 @@ export interface McpUiAppResourceConfig extends ResourceMetadata {
|
|||
* "update-quantity",
|
||||
* {
|
||||
* description: "Update item quantity in cart",
|
||||
* inputSchema: { itemId: z.string(), quantity: z.number() },
|
||||
* inputSchema: z.object({ itemId: z.string(), quantity: z.number() }),
|
||||
* _meta: {
|
||||
* ui: {
|
||||
* resourceUri: "ui://shop/cart.html",
|
||||
|
|
|
|||
Loading…
Reference in a new issue