Repository navigation
feat(agents): make @modelcontextprotocol/sdk and /server optional peers - #2634
mattzcarey wants to merge 4 commits into
Conversation
Fixes #2209. The root entry reached the v1 SDK at runtime through the RPC server transport, and its types through McpAgent and the v1 client invoker. Split RPCServerTransport into mcp/rpc-server.ts, type the RPC client against a structural McpRpcAgent, and move the v1/v2 client binder used by x402 into invoker-compat.ts. Adds a bundle test so the root, client, react, and mcp/client entries stay free of both packages.
🦋 Changeset detectedLatest commit: dba5454 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🟢 agents import sizes: 4 entry points changed, no growth
Changed exports (43)
How this worksEach runtime export is bundled on its own, minified, and gzipped. Changes smaller than 100 B, or smaller than 1% and 1 KiB, are ignored. Growth over 10% or 5 KiB is marked 🔴. This report is informational and does not fail CI. The workflow artifact contains every measurement. Compared |
| "@modelcontextprotocol/server": { | ||
| "optional": true |
There was a problem hiding this comment.
🔴 Legacy MCP servers require both peers
Installing only the documented v1 peer makes McpAgent fail to load. McpAgent eagerly imports @modelcontextprotocol/server, which the new optional peer declaration no longer installs.
Learn more
The legacy agents/mcp entry re-exports McpAgent through the compatibility barrel. McpAgent imports v2 server constructors at module initialization for its version check, and the legacy handler barrel also re-exports a compatibility handler that imports v2 code. Marking the v2 peer optional lets npm omit it when users install only the documented v1 SDK, so a Worker importing agents/mcp fails before serving any request.
Example: An application installs agents and @modelcontextprotocol/sdk@1.32.1, then imports McpAgent from agents/mcp. Its Worker cannot load because @modelcontextprotocol/server is absent, although it uses only a v1 server.
Recommended fix: Remove eager v2 imports from the legacy entry, including the instanceof checks in McpAgent and the stateless handler re-export chain. Keep v2-dependent functionality behind the separate v2 entry or an explicit opt-in boundary; test importing and deploying agents/mcp with only the v1 peer installed.
Was this helpful? React with 👍 or 👎 to provide feedback.
| "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). |
There was a problem hiding this comment.
| The two server paths use different MCP SDK packages, and both are optional peer dependencies of `agents`. Install only the one you use: | ||
|
|
||
| | Package | Needed for | | ||
| | ------------------------------------ | ------------------------------------------------------------------------------------------------------ | | ||
| | `@modelcontextprotocol/server@2.3.1` | `createMcpHandler()` from `agents/mcp/server` | | ||
| | `@modelcontextprotocol/sdk@1.32.1` | `McpAgent`, `WorkerTransport`, `createLegacyMcpHandler()`, and the other SDK v1 APIs from `agents/mcp` | | ||
|
|
||
| The `agents` root entry and the MCP client in `agents/mcp/client` do not need either package. |
There was a problem hiding this comment.
| "@modelcontextprotocol/server": { | ||
| "optional": true | ||
| }, |
There was a problem hiding this comment.
🔴 Legacy MCP servers require an undocumented peer
When an app installs only the documented v1 SDK, importing McpAgent fails without @modelcontextprotocol/server. McpAgent loads v2 classes at runtime, so the legacy server cannot start.
Learn more
The legacy agents/mcp entry imports McpAgent, which loads the v2 package to reject v2 servers. The same entry re-exports createMcpHandler, whose compatibility dispatcher imports the v2 handler. Marking v2 optional means an installation containing only the documented v1 peer cannot load the legacy entry at all.
Example: An existing app upgrades agents, adds @modelcontextprotocol/sdk@1.32.1 as instructed, and does not install @modelcontextprotocol/server. Loading its McpAgent class fails while resolving the missing v2 package, before it can serve requests.
Recommended fix: Remove eager v2 runtime imports from the legacy entry when v2 is absent, including the instanceof check and compatibility dispatcher. Preserve the v2 overload through an isolated entry or another approach that only loads v2 after the caller selects that path. Test bundling and loading agents/mcp with v1 installed and v2 absent.
Was this helpful? React with 👍 or 👎 to provide feedback.
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
@modelcontextprotocol/sdk(v1) and@modelcontextprotocol/server(v2) are now optional peers ofagents, so apps that do not serve MCP stop installing the v1 server stack (express,cors,raw-body,@hono/node-server, ...). The rootagentsentry andagents/mcp/clientnow load neither package, at runtime or in their.d.ts. Fixes #2209.Marking them optional was not enough on its own, because the root entry reached the v1 SDK through the MCP client:
src/mcp/rpc.tsheld both RPC transports.RPCServerTransportimported v1 values, and the client chunk pulled it in. It now lives insrc/mcp/rpc-server.ts, used only byMcpAgent.addMcpServer(name, binding)andRPCClientTransportOptionswere typed againstMcpAgent, which dragged the whole legacy server into the root types. They now use a structuralMcpRpcAgent(anAgentwithhandleMcpMessage). It accepts the same classes as before.agents/x402uses moved tosrc/mcp/client/invoker-compat.ts. The deprecated legacy result-schema overload ofMCPClientManager.callToolnow takes any{ parse }schema instead of the v1 zod type.@modelcontextprotocol/serveragents/mcp/server(createMcpHandler)@modelcontextprotocol/sdkagents/mcp(McpAgent,WorkerTransport,createLegacyMcpHandler),agents/experimental/webmcpagents/mcp/clientnow also exportsRPCClientTransport,RPC_DO_PREFIX,McpClientOptionsand the elicitation handler types, so apps that took those from theagents/mcpbarrel can move off it and drop the v1 SDK.@modelcontextprotocol/clientandzodstay required, because everyAgentconstructs the MCP client. The changeset isminor: npm users who use the legacy APIs and relied on the auto-installed peer without declaring it must now add@modelcontextprotocol/sdkthemselves.McpAgentstays.Docs updated in
docs/agents/mcp-servers.md,mcp-transports.md,mcp-client.mdand the package README. cloudflare-docs: cloudflare/cloudflare-docs#34485.src/node-tests/optional-mcp-peers-bundle.test.tsbundlesagents,agents/client,agents/reactandagents/mcp/clientand fails if either package is reached. It fails onmain.agentsinstalled: no v1 SDK orexpressinnode_modules, andwrangler deploy --dry-runbuilds anAgentthat callsaddMcpServer.pnpm run typecheck,check:exports, and the workers MCP, x402, webmcp and node suites ran.