传输方式
Model Context Protocol (MCP) 规范定义了两种标准传输机制 ↗用于客户端与服务器之间的通信:
- stdio — 通过标准输入和标准输出通信,设计用于本地 MCP 连接。
- Streamable HTTP — 远程 MCP 连接的标准传输方式,于 2025 年 3 月 引入 ↗。它使用单个 HTTP 端点完成双向通信。
注意
Server-Sent Events (SSE) 之前用于远程 MCP 连接,但已被弃用,推荐使用 Streamable HTTP。如果需要为旧版客户端提供 SSE 支持,请使用 McpAgent 类。
使用 Agents SDK 构建的 MCP 服务器通过 createMcpHandler 处理 Streamable HTTP 传输。
实现远程 MCP 传输
使用 createMcpHandler 创建一个支持 Streamable HTTP 传输的 MCP 服务器。这是新建 MCP 服务器的推荐方式。
快速开始
可以使用 “Deploy to Cloudflare” 按钮一键创建一个远程 MCP 服务器。
远程 MCP 服务器(无认证)
使用 createMcpHandler 创建一个 MCP 服务器。完整示例参见 GitHub ↗。
JavaScript
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "My MCP Server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Returns a greeting message",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => {
return {
content: [{ text: `Hello, ${name ?? "World"}!`, type: "text" }],
};
},
);
return server;
}
export default {
fetch: (request, env, ctx) => {
// Create a new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
};
TypeScript
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "My MCP Server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Returns a greeting message",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => {
return {
content: [{ text: `Hello, ${name ?? "World"}!`, type: "text" }],
};
},
);
return server;
}
export default {
fetch: (request: Request, env: Env, ctx: ExecutionContext) => {
// Create a new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
} satisfies ExportedHandler<Env>;
带认证的 MCP 服务器
如果你的 MCP 服务器使用 Workers OAuth Provider ↗ 库实现认证与授权,请通过 apiRoute 和 apiHandler 属性使用 createMcpHandler。完整示例参见 GitHub ↗。
JavaScript
export default new OAuthProvider({
apiRoute: "/mcp",
apiHandler: {
fetch: (request, env, ctx) => {
// Create a new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
},
// ... other OAuth configuration
});
TypeScript
export default new OAuthProvider({
apiRoute: "/mcp",
apiHandler: {
fetch: (request: Request, env: Env, ctx: ExecutionContext) => {
// Create a new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
},
// ... other OAuth configuration
});
有状态的 MCP 服务器
如果你的 MCP 服务器需要跨请求维护状态,可在一个 Agent 类中使用 createMcpHandler 配合 WorkerTransport。这样可以把会话状态持久化到 Durable Object 存储中,并使用 elicitation ↗ 和 sampling ↗ 等高级 MCP 特性。
实现细节参见有状态 MCP 服务器。
RPC 传输
RPC 传输专为内部应用设计,适用于 MCP 服务器和 agent 都运行在 Cloudflare 上(它们甚至可以运行在同一个 Worker 中)的场景。它通过 Cloudflare 的 RPC bindings 直接发送 JSON-RPC 消息,完全不经公网。
- 更快 — 无网络开销,Durable Object 之间直接函数调用
- 更简单 — 无 HTTP 端点,无连接管理
- 仅供内部使用 — 非常适合同一 Worker 内 agent 调用 MCP 服务器
RPC 传输不支持身份验证。需要 OAuth 的外部连接请使用 Streamable HTTP。
通过 RPC 把 Agent 连接到 McpAgent
1. 定义你的 MCP 服务器
创建一个 McpAgent,暴露你想要提供的工具:
JavaScript
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCP extends McpAgent {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
initialState = { counter: 0 };
async init() {
this.server.tool(
"add",
"Add to the counter",
{ amount: z.number() },
async ({ amount }) => {
this.setState({ counter: this.state.counter + amount });
return {
content: [
{
type: "text",
text: `Added ${amount}, total is now ${this.state.counter}`,
},
],
};
},
);
}
}
TypeScript
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
type State = { counter: number };
export class MyMCP extends McpAgent<Env, State> {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
initialState: State = { counter: 0 };
async init() {
this.server.tool(
"add",
"Add to the counter",
{ amount: z.number() },
async ({ amount }) => {
this.setState({ counter: this.state.counter + amount });
return {
content: [
{
type: "text",
text: `Added ${amount}, total is now ${this.state.counter}`,
},
],
};
},
);
}
}
2. 把 Agent 连接到 MCP 服务器
在你的 Agent 中,在 onStart() 内用 Durable Object binding 调用 addMcpServer():
JavaScript
import { AIChatAgent } from "@cloudflare/ai-chat";
export class Chat extends AIChatAgent {
async onStart() {
// Pass the DO namespace binding directly
await this.addMcpServer("my-mcp", this.env.MyMCP);
}
async onChatMessage(onFinish) {
const allTools = this.mcp.getAITools();
const result = streamText({
model,
tools: allTools,
// ...
});
return createUIMessageStreamResponse({ stream: result });
}
}
TypeScript
import { AIChatAgent } from "@cloudflare/ai-chat";
export class Chat extends AIChatAgent<Env> {
async onStart(): Promise<void> {
// Pass the DO namespace binding directly
await this.addMcpServer("my-mcp", this.env.MyMCP);
}
async onChatMessage(onFinish) {
const allTools = this.mcp.getAITools();
const result = streamText({
model,
tools: allTools,
// ...
});
return createUIMessageStreamResponse({ stream: result });
}
}
RPC 连接在 Durable Object 休眠之后会自动恢复,与 HTTP 连接一样。binding 名称和 props 会被持久化到存储中,无需任何额外代码即可重新建立连接。
对于 RPC 传输,如果用同一个名称调用 addMcpServer 而该名称已有活动连接,会直接返回已有连接,而不会重复创建。对于 HTTP 传输,去重会同时匹配服务器名和 URL(参见 MCP Client API)。这让你在 onStart() 中调用它时是安全的。
3. 配置 Durable Object bindings
在 wrangler.jsonc 中,为两个 Durable Object 都定义 binding:
JSONC
{
"durable_objects": {
"bindings": [
{ "name": "Chat", "class_name": "Chat" },
{ "name": "MyMCP", "class_name": "MyMCP" }
]
},
"migrations": [
{
"new_sqlite_classes": ["MyMCP", "Chat"],
"tag": "v1"
}
]
}
4. 设置 Worker 的 fetch 处理函数
把请求路由到你的 Chat agent:
JavaScript
import { routeAgentRequest } from "agents";
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Optionally expose the MCP server via HTTP as well
if (url.pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
const response = await routeAgentRequest(request, env);
if (response) return response;
return new Response("Not found", { status: 404 });
},
};
TypeScript
import { routeAgentRequest } from "agents";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
// Optionally expose the MCP server via HTTP as well
if (url.pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
const response = await routeAgentRequest(request, env);
if (response) return response;
return new Response("Not found", { status: 404 });
},
} satisfies ExportedHandler<Env>;
向 MCP 服务器传递 props
由于 RPC 传输没有 OAuth 流程,你可以直接把用户上下文作为 props 传过去:
JavaScript
await this.addMcpServer("my-mcp", this.env.MyMCP, {
props: { userId: "user-123", role: "admin" },
});
TypeScript
await this.addMcpServer("my-mcp", this.env.MyMCP, {
props: { userId: "user-123", role: "admin" },
});
你的 McpAgent 可以这样访问这些 props:
JavaScript
export class MyMCP extends McpAgent {
async init() {
this.server.tool("whoami", "Get current user info", {}, async () => {
const userId = this.props?.userId || "anonymous";
const role = this.props?.role || "guest";
return {
content: [{ type: "text", text: `User ID: ${userId}, Role: ${role}` }],
};
});
}
}
TypeScript
export class MyMCP extends McpAgent<
Env,
State,
{ userId?: string; role?: string }
> {
async init() {
this.server.tool("whoami", "Get current user info", {}, async () => {
const userId = this.props?.userId || "anonymous";
const role = this.props?.role || "guest";
return {
content: [
{ type: "text", text: `User ID: ${userId}, Role: ${role}` },
],
};
});
}
}
Props 是类型安全的(TypeScript 会从你的 McpAgent 泛型中提取出 Props 类型),会持久化(存于 Durable Object 存储),并且在任何工具被调用前就已可用。
配置 RPC 传输的服务器超时
RPC 传输的工具响应等待时间是可配置的。默认情况下,服务器会等待工具处理函数响应 60 秒。你可以在 McpAgent 中重写 getRpcTransportOptions() 来自定义:
JavaScript
export class MyMCP extends McpAgent {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
getRpcTransportOptions() {
return { timeout: 120000 }; // 2 minutes
}
async init() {
this.server.tool(
"long-running-task",
"A tool that takes a while",
{ input: z.string() },
async ({ input }) => {
await longRunningOperation(input);
return {
content: [{ type: "text", text: "Task completed" }],
};
},
);
}
}
TypeScript
export class MyMCP extends McpAgent<Env, State> {
server = new McpServer({ name: "MyMCP", version: "1.0.0" });
protected getRpcTransportOptions() {
return { timeout: 120000 }; // 2 minutes
}
async init() {
this.server.tool(
"long-running-task",
"A tool that takes a while",
{ input: z.string() },
async ({ input }) => {
await longRunningOperation(input);
return {
content: [{ type: "text", text: "Task completed" }],
};
},
);
}
}
选择传输方式
| 传输方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Streamable HTTP | 外部 MCP 服务器、生产应用 | 标准协议,安全,支持认证 | 略有网络开销 |
| RPC | Cloudflare 上的内部 agent | 最快,搭建最简单 | 无认证,仅限 Durable Object binding |
| SSE | 旧版兼容 | 向后兼容 | 已弃用,请使用 Streamable HTTP |
从 McpAgent 迁移
如果你已有使用 McpAgent 类的 MCP 服务器:
- 不使用状态? 把你的
McpAgent类替换为@modelcontextprotocol/sdk的McpServer,在 Worker 的fetch处理函数中使用createMcpHandler(server)。 - 使用状态? 在一个 Agent 类中使用
createMcpHandler配合WorkerTransport。详见有状态 MCP 服务器。 - 需要 SSE 支持? 继续用
McpAgent配合serveSSE(),以兼容旧版客户端。详见 McpAgent API 参考。
用 MCP 客户端测试
你可以使用支持远程连接的 MCP 客户端来测试你的 MCP 服务器,也可以使用 mcp-remote ↗——一个让仅支持本地连接的 MCP 客户端能与远程 MCP 服务器协作的适配器。
按照这个指南了解如何把远程 MCP 服务器连接到 Claude Desktop、Cursor、Windsurf 以及其他 MCP 客户端。