本指南介绍 Agents SDK v0.20.0 中的 MCP SDK v2 ↗ 升级。说明如何将服务器迁移到 @modelcontextprotocol/server,仅在需要有会话特性时使用临时旧版通道,以及如何更新 MCP 客户端。
使用下表选择迁移路径:
| 当前服务器 | 迁移路径 |
|---|---|
| 无会话依赖的 SDK v1 服务器 | 将服务器定义移到 SDK v2 工厂函数,并把工厂函数传给 createMcpHandler。 |
| 依赖有会话特性的 SDK v1 服务器 | 添加 SDK v2 路由。仅在替换这些依赖期间,在临时旧版通道上保留 createLegacyMcpHandler。 |
不使用旧版有状态特性的 McpAgent |
直接迁移到 SDK v2 工厂函数与 createMcpHandler。 |
使用旧版有状态特性的 McpAgent |
设计无状态等价方案,同时提供无状态与旧版通道,然后排空旧版通道。 |
Agents SDK v0.20.0 弃用以下 API:
- 将 SDK v1 服务器传给
createMcpHandler。请将服务器移到 SDK v2 工厂函数。仅将createLegacyMcpHandler作为有会话行为的临时桥接。该重载计划在下一个主版本中移除。 McpAgent。已弃用且功能冻结。请尽早迁移。尚未宣布移除版本。MCPClientManager.callTool(params, resultSchema, options)与withX402Client(...).callTool(confirm, params, resultSchema, options)。请改用callTool(params, options)或callTool(confirm, params, options)。尚未宣布移除版本。
experimental_createMcpHandler 此前已弃用,仍计划在下一个主版本中移除。请将其 SDK v1 服务器移到 SDK v2 工厂函数。仅在迁移期间于临时有会话通道上使用 createLegacyMcpHandler。
仅安装应用实际导入的 MCP 包世代。保持 v2 版本精确固定。
无状态服务器:
npm i agents @modelcontextprotocol/[email protected] zodyarn add agents @modelcontextprotocol/[email protected] zodpnpm add agents @modelcontextprotocol/[email protected] zodbun add agents @modelcontextprotocol/[email protected] zod临时旧版通道:
npm i agents @modelcontextprotocol/[email protected] zodyarn add agents @modelcontextprotocol/[email protected] zodpnpm add agents @modelcontextprotocol/[email protected] zodbun add agents @modelcontextprotocol/[email protected] zod连接 MCP 服务器的 Agent:
npm i agents @modelcontextprotocol/[email protected]yarn add agents @modelcontextprotocol/[email protected]pnpm add agents @modelcontextprotocol/[email protected]bun add agents @modelcontextprotocol/[email protected]@modelcontextprotocol/server 和 @modelcontextprotocol/sdk 是 agents 的可选对等依赖,因此包管理器不会自动安装它们。使用时请将它们添加到自己的 package.json。@modelcontextprotocol/client 是必需的对等依赖,因为每个 Agent 都包含 MCP 客户端。请随支持这些版本的 Agents 发行版更新 MCP 的具体版本。
如果现有旧版服务器导入了 @modelcontextprotocol/sdk,却没有在 package.json 中声明它,那么它此前只是因为 agents 将该包作为依赖安装才得以运行。升级前请显式添加此依赖。
即使只从 agents/mcp 导入客户端辅助函数,该入口也会加载 @modelcontextprotocol/sdk。请改为从 agents/mcp/client 导入 RPCClientTransport、RPC_DO_PREFIX 和 McpClientOptions。agents/x402 也需要安装 @modelcontextprotocol/sdk:它在运行时不会加载该包,但其类型引用 MCP SDK v1。
不要仅因为服务器当前导入 SDK v1 就继续保留它。除非端点依赖以下有会话特性之一,否则请直接迁移到 SDK v2 工厂函数:
- 协议会话或传入的
WorkerTransport - 传输存储或事件重放
- 独立 GET 流
- 推送的 elicitation、sampling 或 roots 请求
- 使用 HTTP
DELETE的会话删除
若端点使用上述任一特性,请先部署无状态路由。仅在替换有会话依赖期间保留 SDK v1 路由。使用 isLegacyRequest() 路由请求,如 同时运行无状态与旧版通道 所示。
对于不使用 McpAgent 的 SDK v1 端点,仅在该临时旧版分支上使用 createLegacyMcpHandler。客户端完成迁移且现有会话排空后将其移除。
无状态 createMcpHandler 接受工厂函数。工厂函数返回来自 @modelcontextprotocol/server 的 McpServer 或 Server。
-
按照上游 TypeScript SDK v2 迁移指南 ↗ 了解服务器注册变更。
-
从
@modelcontextprotocol/server导入服务器。 -
将服务器构造与注册移入工厂函数。
-
保留 Worker 对象的 default export。在其
fetch()方法中,将工厂函数传给createMcpHandler,并像以前一样调用返回的可调用对象。仅在较低层请求集成时使用处理程序的fetch(request, options?)方法。 -
不要将 Agents 处理程序返回的可调用对象作为 default export。Wrangler 会把函数形式的 default export 解释为
WorkerEntrypoint类。 -
移除 SDK v1 的传输与会话选项。
-
用无状态客户端与旧版客户端测试该端点。
import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "example-server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Return a greeting",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name ?? "World"}!` }],
}),
);
return server;
}
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer)(request, env, ctx);
},
};import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "example-server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Return a greeting",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name ?? "World"}!` }],
}),
);
return server;
}
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer)(request, env, ctx);
},
} satisfies ExportedHandler;Worker 入口点仍是对象。仅处理程序调用方式发生变化:
// SDK v1: pass a fresh constructed server.
return createMcpHandler(createServer())(request, env, ctx);
// SDK v2: pass the factory itself.
return createMcpHandler(createServer)(request, env, ctx);不要简化为 export default createMcpHandler(createServer)。Agents 处理程序可在另一个处理程序内组合调用,但 Wrangler 会把任何函数 default export 当作 WorkerEntrypoint 类。
SDK v2 处理程序为每个 MCP 请求创建一台服务器。并发 Worker 请求不会共享已连接的服务器实例。
Agents 包装器添加 route、corsOptions、allowedHostnames、allowedOriginHostnames 与 authContext。它还会将受支持的 SDK v2 选项透传给上游处理程序。
常见选项包括:
| 选项 | 行为 |
|---|---|
route |
设置精确请求路径。默认为 /mcp。 |
legacy |
默认使用旧版兼容。无状态专用端点请设为 "reject"。 |
responseMode |
选择自动、JSON 或 SSE 响应处理。 |
allowedHostnames |
将 Host 头限制为特定主机名。 |
allowedOriginHostnames |
限制浏览器 Origin,或在受信任中间件已校验时接受 "*"。 |
corsOptions |
控制 CORS 响应头。设为 false 可移除它们。 |
onerror |
报告处理程序错误而不改变响应。 |
maxSubscriptions, keepAliveMs |
配置 subscriptions/listen 投递。 |
无状态处理程序拒绝以下 SDK v1 选项:
transportstoragesessionIdGeneratoronsessioninitialized与onsessionclosedenableJsonResponseeventStoreallowedHosts与allowedOriginsenableDnsRebindingProtectionretryInterval
请用 responseMode: "json" 替代 enableJsonResponse: true。JSON 模式会丢弃最终结果之前发出的通知。
Workers 包装器会校验每一个存在的 Origin。格式错误、不透明以及非 HTTP Origin 会以 403 拒绝。
默认允许列表包含 localhost 类 Origin 以及端点的 workers.dev 主机名。具体的 corsOptions.origin 也会自动加入其主机名。处理程序会对 localhost 与 workers.dev 端点应用匹配的 Host 检查。
对于使用通配 CORS 的自定义域,请显式配置 Host 与 Origin 限制:
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer, {
allowedHostnames: ["mcp.example.com"],
allowedOriginHostnames: ["app.example.com"],
corsOptions: { origin: "https://app.example.com" },
})(request, env, ctx);
},
};export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer, {
allowedHostnames: ["mcp.example.com"],
allowedOriginHostnames: ["app.example.com"],
corsOptions: { origin: "https://app.example.com" },
})(request, env, ctx);
},
} satisfies ExportedHandler;仅在受信任中间件于调用处理程序之前校验 Origin 时,才设置 allowedOriginHostnames: "*"。该值会关闭处理程序的 Origin 检查。MCP HTTP 服务器必须校验浏览器 Origin。
CORS 头并不认证请求。请用 OAuth 或其他身份验证层保护端点。
处理程序不会从 request.url 推断受信任的 Host 允许列表。若部署接受任意 Host 值,请在调用处理程序之前校验它们。对于 Cloudflare Workers 之外的本地服务器,请遵循上游 SDK 的 Host 与 Origin 校验指引。
默认的 legacy: "stateless" 设置支持普通旧版工具、资源与提示词。该通道使用 SDK v2 的 web-standard 传输。它不导入 WorkerTransport,也不是完整的有会话传输。
回退有以下限制:
- 每个 POST 都会获得新的服务器与传输。
- HTTP GET 与 DELETE 返回
405。 - 不持久化 MCP 会话 ID 或协议会话状态。
- 推送的 sampling、elicitation 与 roots 请求会立即失败。
- 独立流、事件重放与会话删除不可用。
- 已发布的实验性 tasks 不通过此回退支持。
迁移这些特性期间,将受影响的旧版客户端路由到临时的 createLegacyMcpHandler 或 McpAgent 通道。
迁移期间,McpAgent 仍是 SDK v1 服务器。不要将旧版路由内的服务器导入改为 @modelcontextprotocol/server。
若服务器不依赖 MCP 会话状态、RPC、推送的服务器到客户端请求、独立流或事件重放,请将其工具移到 SDK v2 工厂函数,并用 createMcpHandler 提供服务。
若服务器使用旧版有状态特性,请在设计并部署无状态等价方案期间保留现有 McpAgent 路由:
| 旧版路径上的有状态特性 | 无状态路径上的设计 |
|---|---|
| 以 MCP 会话为键的应用数据 | 将数据存储在显式应用边界之后,例如 Durable Object、D1、KV 或 R2。使用经过身份验证、由服务器签发的句柄寻址,而不是 MCP 会话 ID。 |
| 多步交互状态 | 随 input_required 返回完整性受保护的 requestState。将其绑定到已认证用户、原始方法与参数,以及过期时间。 |
| 推送的 elicitation、sampling 或 roots 请求 | 返回 inputRequired(...)。客户端完成嵌入式请求后重试原始操作。 |
| 独立的列表变更流 | 通过 subscriptions/listen 发布变更。流结束后客户端重新打开订阅。 |
| 会话重放或传输恢复 | 使每个无状态请求可独立恢复。将业务进度持久化到应用存储,而不是 MCP 传输。 |
Agent 到 McpAgent 的 RPC |
通过 Streamable HTTP 使用 URL 将 Agent 连接到 createMcpHandler 服务器。无状态服务器没有 RPC 传输方式。 |
无状态实现就绪后不要立即移除旧版路由。在客户端迁移与现有会话排空期间同时提供两条通道。
单个 URL 可将无状态请求路由到 SDK v2,将旧版请求路由到现有有会话服务器。
import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
const stateless = createMcpHandler(createStatelessServer, {
route: "/mcp",
legacy: "reject",
});
const legacy = MyMcpAgent.serve("/mcp");
export default {
async fetch(request, env, ctx) {
if (await isLegacyRequest(request)) {
return legacy.fetch(request, env, ctx);
}
return stateless(request, env, ctx);
},
};import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
const stateless = createMcpHandler(createStatelessServer, {
route: "/mcp",
legacy: "reject",
});
const legacy = MyMcpAgent.serve("/mcp");
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
if (await isLegacyRequest(request)) {
return legacy.fetch(request, env, ctx);
}
return stateless(request, env, ctx);
},
} satisfies ExportedHandler<Env>;在无状态处理程序上保持 legacy: "reject"。否则其旧版兼容通道会在有会话路由收到请求之前消费它们。
在迁移客户端之前先部署两条路由。监控旧版通道并让现有会话排空。仅在没有客户端依赖后再移除旧版路由及其仅用于协议的 Durable Object 绑定。将 Durable Object 迁移配置作为单独的部署步骤处理。
Agents 现在在内部使用 @modelcontextprotocol/client。现有 addMcpServer 调用会自动协商协议世代。
无状态路径上的服务器使用 server/discover。对于旧版 Streamable HTTP、SSE 与 RPC 服务器,Agents 会回退到 initialize。
客户端 API 包含这些变更:
callTool(params, options)是首选签名。callTool(params, resultSchema, options)仍可用但已弃用。- MCP 客户端类型现在来自
@modelcontextprotocol/client。 - 所需的无状态 HTTP 头由 SDK 处理。
- 列表变更根据协商到的通道使用无状态订阅或旧版通知。
无状态路径上的工具、提示词与资源可通过多轮请求(MRTR)返回 input_required。SDK 调用已配置的 elicitation 处理程序并重试原始操作。你原来的 callTool、getPrompt 或 readResource Promise 会保持 pending。
每次重试只包含紧邻上一轮输入的响应,而不是此前所有响应。客户端还会回显最新的不透明 requestState。将后续轮次所需的受信任中间值封入完整性受保护的 requestState;不要期望 inputResponses 跨轮次累积。
两轮工具流程请参阅 无状态 elicitation 示例 ↗。
export class MyAgent extends Agent {
onStart() {
this.mcp.configureElicitationHandlers({
form: async (request, serverId, signal) => {
return collectInput(request, serverId, signal);
},
url: async (request, serverId, signal) => {
return openExternalFlow(request, serverId, signal);
},
});
}
}export class MyAgent extends Agent<Env> {
onStart() {
this.mcp.configureElicitationHandlers({
form: async (request, serverId, signal) => {
return collectInput(request, serverId, signal);
},
url: async (request, serverId, signal) => {
return openExternalFlow(request, serverId, signal);
},
});
}
}处理程序与进行中的调用保留在内存中。休眠、isolate 重启、传输丢失或连接重建会使活跃的交互式调用被拒绝。连接恢复后请重试该操作。
将手动处理的 requestState 视为不可信输入。将其绑定到已认证用户与操作,保护完整性,并设置较短过期时间。
自定义 AgentMcpOAuthProvider 必须实现 v2 OAuthClientProvider 契约:
- 从
@modelcontextprotocol/client导入 OAuth 类型。 - 存储
StoredOAuthClientInformation与StoredOAuthTokens。 - 保留凭据上的 SDK issuer 戳记。
- 跨浏览器重定向持久化
OAuthDiscoveryState。 - 在
invalidateCredentials中接受"discovery"。 - 当授权 issuer 不同时,分开保存凭据。
SDK v2 默认校验 OAuth 元数据 issuer。受信任但元数据已知不匹配的旧版服务器可使用 skipIssuerMetadataValidation: true。这会削弱 OAuth mix-up 保护,不应作为通用回退。
MCP 的无状态模型改变了传输与生命周期:
| 领域 | 先前行为 | 新行为 |
|---|---|---|
| 启动 | initialize 握手 |
无握手。server/discover 对客户端可选 |
| 请求元数据 | 连接范围的协商 | 每个请求携带版本、客户端能力与身份元数据 |
| 会话 | 可选 Mcp-Session-Id |
无协议会话 |
| 服务器输入请求 | 服务器发送 JSON-RPC 请求 | 服务器返回 input_required。客户端重试原始操作 |
| 变更通知 | 独立 GET 流与列表变更通知 | 带 SSE 响应的 subscriptions/listen POST |
| 流恢复 | Last-Event-ID 可恢复已配置的流 |
listen 流在失败后重新打开。没有 Last-Event-ID 重放。 |
自定义传输、代理与网关必须保留草案请求头:
MCP-Protocol-VersionMcp-Method- 工具、提示词与资源操作的
Mcp-Name - 声明的
Mcp-Param-*工具头
Agents 使用的精确 SDK 版本实现 MCP 2026-07-28 修订。它使 clientInfo 变为可选,并将服务器身份放在结果 _meta 中。请使用高层 SDK API,并随支持各协议修订的 Agents 发行版更新 MCP 包。原始无状态结果必须包含 resultType。
该草案弃用 Roots、Sampling、Logging、旧的 HTTP+SSE 传输以及 Dynamic Client Registration。在弃用窗口内这些类型仍可用于旧版兼容。已发布的实验性 task 方法成为 io.modelcontextprotocol/tasks 扩展。Agents SDK v0.20.0 未添加该扩展。
这些集成接受 SDK v2 McpServer。请使用 createMcpHandler 提供服务:
- 服务端
withX402辅助函数,来自[email protected]。请参阅为 MCP 工具收费。 - OpenAI Apps SDK 服务器。请参阅 ChatGPT 应用演示。
这些集成仍会生成或要求 SDK v1 服务器:
- Code Mode 的
codeMcpServer和openApiMcpServer辅助函数。请参阅构建 Code Mode MCP 服务器。 mppx的Transport.mcpSdk()支付传输。请参阅接受付款。
在它们生成 SDK v2 服务器之前,请使用 createLegacyMcpHandler 提供其输出,并为每个请求创建一个新服务器。Code Mode MCP 连接器和 withX402Client 同时接受两代客户端。
-
按有会话依赖对每个端点分类。
-
固定 Agents 发行版所需的 MCP SDK 版本。
-
将可无状态的每个服务器定义移到 SDK v2 工厂函数。
-
在仍需临时旧版通道的端点旁添加无状态处理程序。
-
使用
isLegacyRequest()路由无状态与旧版请求。 -
分别测试无状态客户端与旧版客户端。
-
通过每个代理与网关测试所需 HTTP 头。
-
在干净登录后以及 Durable Object 休眠后测试 OAuth。
-
测试取消、多轮输入与传输丢失。
-
验证有效 Origin,并以
403拒绝无效 Origin。 -
仅在现有会话排空后移除旧版路由。
Agents v0.20.0 之前发行版存储的 HTTP 会话 ID 不包含协商的协议版本。升级后的客户端会丢弃这些 ID 并重新连接,而不是发送不安全的恢复请求。绑定到旧远程会话的现有进行中工作不会恢复。
API 详情请参阅 createMcpHandler 和 MCP Client。