Repository navigation
feat(agents): make @modelcontextprotocol/sdk and /server optional peers #2634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8e2c5e6
6f51ba8
68b1d6a
0b743eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "agents": minor | ||
| --- | ||
|
|
||
| `@modelcontextprotocol/sdk` and `@modelcontextprotocol/server` are now optional peer dependencies, so apps that do not serve MCP no longer install the SDK v1 server stack (`express`, `cors`, `@hono/node-server`, and others). Install `@modelcontextprotocol/server` for `agents/mcp/server`, and `@modelcontextprotocol/sdk` for `McpAgent`, `WorkerTransport`, `createLegacyMcpHandler`, and `agents/experimental/webmcp`. The root `agents` entry and `agents/mcp/client` need neither. If your app imports `@modelcontextprotocol/sdk` without listing it in `package.json`, add it before upgrading. The `agents/mcp` barrel needs `@modelcontextprotocol/sdk` even when you only import client helpers from it, so import those from `agents/mcp/client` instead. See [Creating MCP Servers](https://github.com/cloudflare/agents/blob/main/docs/agents/mcp-servers.md#choosing-an-approach). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔍 Legacy server installation guidance is absent Existing Was this helpful? React with 👍 or 👎 to provide feedback. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -109,6 +109,12 @@ | |
| "@earendil-works/pi-durable": { | ||
| "optional": true | ||
| }, | ||
| "@modelcontextprotocol/sdk": { | ||
| "optional": true | ||
| }, | ||
| "@modelcontextprotocol/server": { | ||
| "optional": true | ||
|
Comment on lines
+115
to
+116
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Legacy MCP servers require both peers Installing only the documented v1 peer makes Learn moreThe legacy Example: An application installs Recommended fix: Remove eager v2 imports from the legacy entry, including the Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| }, | ||
|
Comment on lines
+115
to
+117
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Legacy MCP servers require an undocumented peer When an app installs only the documented v1 SDK, importing Learn moreThe legacy Example: An existing app upgrades Recommended fix: Remove eager v2 runtime imports from the legacy entry when v2 is absent, including the Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| "@opencode/plugin": { | ||
| "optional": true | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import type { | ||
| CallToolRequest, | ||
| CallToolRequestOptions, | ||
| Client, | ||
| ListToolsResult | ||
| } from "@modelcontextprotocol/client"; | ||
| import type { Client as LegacyClient } from "@modelcontextprotocol/sdk/client/index.js"; | ||
| import type { | ||
| CallToolResult as LegacyCallToolResult, | ||
| ListToolsRequest | ||
| } from "@modelcontextprotocol/sdk/types.js"; | ||
| import type { RequestOptions as LegacyRequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js"; | ||
| import { | ||
| type CallToolSchemaOrOptions, | ||
| normalizeCallToolArguments | ||
| } from "./invoker"; | ||
|
|
||
| // Accepts both SDK v1 and v2 clients. Kept separate from `./invoker` because | ||
| // its types depend on `@modelcontextprotocol/sdk`, an optional peer that only | ||
| // the x402 entry needs. | ||
|
|
||
| export type CompatibleMcpClient = Client | LegacyClient; | ||
|
|
||
| export interface BoundMcpClientInvoker { | ||
| listTools( | ||
| params?: ListToolsRequest["params"], | ||
| options?: CallToolRequestOptions | ||
| ): Promise<ListToolsResult>; | ||
| callTool( | ||
| params: CallToolRequest["params"], | ||
| schemaOrOptions?: CallToolSchemaOrOptions, | ||
| options?: CallToolRequestOptions | ||
| ): Promise<LegacyCallToolResult>; | ||
| } | ||
|
|
||
| /** | ||
| * Bind the original methods before decorators such as x402 replace them. All | ||
| * generation-specific calling conventions stay behind this small interface. | ||
| */ | ||
| export function bindMcpClient( | ||
| client: CompatibleMcpClient | ||
| ): BoundMcpClientInvoker { | ||
| const isV2 = | ||
| "getProtocolEra" in client && | ||
| typeof (client as { getProtocolEra?: unknown }).getProtocolEra === | ||
| "function"; | ||
|
|
||
| if (isV2) { | ||
| const v2Client = client as Client; | ||
| const listTools = v2Client.listTools.bind(v2Client); | ||
| const callTool = v2Client.callTool.bind(v2Client); | ||
| return { | ||
| listTools: (params, options) => listTools(params, options), | ||
| callTool: (params, schemaOrOptions, options) => { | ||
| const normalized = normalizeCallToolArguments(schemaOrOptions, options); | ||
| const pending = normalized.schema | ||
| ? v2Client.request( | ||
| { method: "tools/call", params }, | ||
| normalized.schema as never, | ||
| normalized.options | ||
| ) | ||
| : callTool(params, normalized.options); | ||
| return pending as Promise<LegacyCallToolResult>; | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| const legacy = client as LegacyClient; | ||
| const listTools = legacy.listTools.bind(legacy); | ||
| const callTool = legacy.callTool.bind(legacy); | ||
| return { | ||
| listTools: (params, options) => | ||
| listTools( | ||
| params, | ||
| options as LegacyRequestOptions | ||
| ) as Promise<ListToolsResult>, | ||
| callTool: (params, schemaOrOptions, options) => { | ||
| const normalized = normalizeCallToolArguments(schemaOrOptions, options); | ||
| return callTool( | ||
| params as Parameters<LegacyClient["callTool"]>[0], | ||
| normalized.schema as Parameters<LegacyClient["callTool"]>[1], | ||
| normalized.options as LegacyRequestOptions | ||
| ) as Promise<LegacyCallToolResult>; | ||
| } | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔍 Changeset duplicates documentation
The repository requires a single concise description linking to documentation. This multi-sentence release note embeds installation details that belong in the linked guide.
Was this helpful? React with 👍 or 👎 to provide feedback.