createMcpHandler
createMcpHandler 函数用于创建一个 fetch handler 来对外提供你的 MCP server。当你需要在普通 Worker(没有 Durable Object)中运行无状态的 MCP server 时使用它。如果需要跨请求保留状态的有状态 MCP server,请改用 McpAgent 类。
它使用基于 Web 标准实现的 MCP Transport 接口 WorkerTransport,该实现符合 streamable-http ↗ 传输规范。
TypeScript
import { createMcpHandler, type CreateMcpHandlerOptions } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
function createMcpHandler(
server: McpServer,
options?: CreateMcpHandlerOptions,
): (request: Request, env: Env, ctx: ExecutionContext) => Promise<Response>;
参数
- server —
@modelcontextprotocol/sdk包中的 McpServer ↗ 实例 - options — 可选的配置对象(参见 CreateMcpHandlerOptions)
返回值
一个 Worker fetch handler 函数,签名为 (request: Request, env: unknown, ctx: ExecutionContext) => Promise<Response>。
CreateMcpHandlerOptions
创建 MCP handler 的配置选项。
TypeScript
interface CreateMcpHandlerOptions extends WorkerTransportOptions {
/**
* The route path that this MCP handler should respond to.
* If specified, the handler will only process requests that match this route.
* @default "/mcp"
*/
route?: string;
/**
* An optional auth context to use for handling MCP requests.
* If not provided, the handler will look for props in the execution context.
*/
authContext?: McpAuthContext;
/**
* An optional transport to use for handling MCP requests.
* If not provided, a WorkerTransport will be created with the provided WorkerTransportOptions.
*/
transport?: WorkerTransport;
// Inherited from WorkerTransportOptions:
sessionIdGenerator?: () => string;
enableJsonResponse?: boolean;
onsessioninitialized?: (sessionId: string) => void;
corsOptions?: CORSOptions;
storage?: MCPStorageApi;
}
Explain Code
选项
route
MCP handler 响应的 URL 路径。请求其他路径将返回 404。
默认值: "/mcp"
JavaScript
const handler = createMcpHandler(server, {
route: "/api/mcp", // Only respond to requests at /api/mcp
});
TypeScript
const handler = createMcpHandler(server, {
route: "/api/mcp", // Only respond to requests at /api/mcp
});
authContext
一个认证上下文对象,会通过 getMcpAuthContext() 提供给 MCP 工具使用。
如果你使用 @cloudflare/workers-oauth-provider 中的 OAuthProvider,认证上下文会自动从 OAuth 流程中填充,通常无需手动设置。
transport
自定义的 WorkerTransport 实例。如果未提供,每次请求都会创建一个新的 transport。
JavaScript
import { createMcpHandler, WorkerTransport } from "agents/mcp";
const transport = new WorkerTransport({
sessionIdGenerator: () => `session-${crypto.randomUUID()}`,
storage: {
get: () => myStorage.get("transport-state"),
set: (state) => myStorage.put("transport-state", state),
},
});
const handler = createMcpHandler(server, { transport });
Explain Code
TypeScript
import { createMcpHandler, WorkerTransport } from "agents/mcp";
const transport = new WorkerTransport({
sessionIdGenerator: () => `session-${crypto.randomUUID()}`,
storage: {
get: () => myStorage.get("transport-state"),
set: (state) => myStorage.put("transport-state", state),
},
});
const handler = createMcpHandler(server, { transport });
Explain Code
无状态 MCP 服务器
许多 MCP 服务器是无状态的,即在请求之间不维护任何会话状态。createMcpHandler 是 McpAgent 类的轻量级替代方案,可直接从 Worker 提供 MCP 服务。完整示例请见 GitHub ↗。
MCP SDK 1.26.0 的破坏性变更
重要: 如果你正从 1.26.0 之前的 MCP SDK 升级,必须更新无状态服务器中创建 McpServer 实例的方式。
MCP SDK 1.26.0 引入了一项保护机制,禁止将服务器实例连接到一个已经连接过的 transport。此举修复了一个安全漏洞 (CVE ↗):共享 server 或 transport 实例可能导致跨客户端响应数据泄露。
如果你的无状态 MCP 服务器在全局作用域中声明了 McpServer 或 transport 实例,你必须改为按请求创建新的实例。
详细内容请参阅下面的 MCP SDK 1.26.0 迁移指南。
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: "Hello MCP Server",
version: "1.0.0",
});
server.tool(
"hello",
"Returns a greeting message",
{ name: z.string().optional() },
async ({ name }) => {
return {
content: [
{
text: `Hello, ${name ?? "World"}!`,
type: "text",
},
],
};
},
);
return server;
}
export default {
fetch: async (request, env, ctx) => {
// Create new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
};
Explain Code
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: "Hello MCP Server",
version: "1.0.0",
});
server.tool(
"hello",
"Returns a greeting message",
{ name: z.string().optional() },
async ({ name }) => {
return {
content: [
{
text: `Hello, ${name ?? "World"}!`,
type: "text",
},
],
};
},
);
return server;
}
export default {
fetch: async (request: Request, env: Env, ctx: ExecutionContext) => {
// Create new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
} satisfies ExportedHandler<Env>;
Explain Code
每次请求该 MCP 服务器都会创建一个新的会话与 server 实例。服务器在请求之间不保留任何状态。这是实现 MCP 服务器最简单的方式。
有状态 MCP 服务器
对于需要跨多个请求维护会话状态的 MCP 服务器,你可以在 Agent 中直接使用 createMcpHandler 配合 WorkerTransport 实例。这在你需要使用诸如 elicitation、sampling 等高级客户端特性时非常有用。
提供一个具备持久化存储的自定义 WorkerTransport。完整示例请见 GitHub ↗。
JavaScript
import { Agent } from "agents";
import { createMcpHandler, WorkerTransport } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const STATE_KEY = "mcp-transport-state";
export class MyStatefulMcpAgent extends Agent {
server = new McpServer({
name: "Stateful MCP Server",
version: "1.0.0",
});
transport = new WorkerTransport({
sessionIdGenerator: () => this.name,
storage: {
get: () => {
return this.ctx.storage.get(STATE_KEY);
},
set: (state) => {
this.ctx.storage.put(STATE_KEY, state);
},
},
});
async onRequest(request) {
return createMcpHandler(this.server, {
transport: this.transport,
})(request, this.env, this.ctx);
}
}
Explain Code
TypeScript
import { Agent } from "agents";
import {
createMcpHandler,
WorkerTransport,
type TransportState,
} from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const STATE_KEY = "mcp-transport-state";
type State = { counter: number };
export class MyStatefulMcpAgent extends Agent<Env, State> {
server = new McpServer({
name: "Stateful MCP Server",
version: "1.0.0",
});
transport = new WorkerTransport({
sessionIdGenerator: () => this.name,
storage: {
get: () => {
return this.ctx.storage.get<TransportState>(STATE_KEY);
},
set: (state: TransportState) => {
this.ctx.storage.put(STATE_KEY, state);
},
},
});
async onRequest(request: Request) {
return createMcpHandler(this.server, {
transport: this.transport,
})(request, this.env, this.ctx as unknown as ExecutionContext);
}
}
Explain Code
在该例中我们将 sessionIdGenerator 定义为返回 Agent 名称作为 session ID。为了确保路由到正确的 Agent,可以在 Worker handler 中使用 getAgentByName:
JavaScript
import { getAgentByName } from "agents";
export default {
async fetch(request, env, ctx) {
// Extract session ID from header or generate a new one
const sessionId =
request.headers.get("mcp-session-id") ?? crypto.randomUUID();
// Get the Agent instance by name/session ID
const agent = await getAgentByName(env.MyStatefulMcpAgent, sessionId);
// Route the MCP request to the agent
return await agent.onRequest(request);
},
};
Explain Code
TypeScript
import { getAgentByName } from "agents";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// Extract session ID from header or generate a new one
const sessionId =
request.headers.get("mcp-session-id") ?? crypto.randomUUID();
// Get the Agent instance by name/session ID
const agent = await getAgentByName(env.MyStatefulMcpAgent, sessionId);
// Route the MCP request to the agent
return await agent.onRequest(request);
},
} satisfies ExportedHandler<Env>;
Explain Code
通过持久化存储,transport 会保留:
- 跨重连的 Session ID
- 协议版本协商状态
- 初始化状态
这让 MCP 客户端在连接丢失后能够重新连接并恢复其会话。
MCP SDK 1.26.0 迁移指南
MCP SDK 1.26.0 为无状态 MCP 服务器引入了破坏性变更,以解决一个严重的安全漏洞:在共享 server 或 transport 实例的情况下,一个客户端的响应可能泄露到另一个客户端。
谁会受影响?
| 服务器类型 | 是否受影响? | 需要的操作 |
|---|---|---|
| 使用 Agent/Durable Object 的有状态服务器 | 否 | 无需更改 |
| 使用 createMcpHandler 的无状态服务器 | 是 | 按请求创建新的 McpServer |
| 使用原生 SDK transport 的无状态服务器 | 是 | 按请求创建新的 McpServer 与 transport |
为什么必须这样做?
之前那种在全局作用域声明 McpServer 实例的写法可能让一个客户端的响应泄露给另一个客户端,这是一个安全漏洞。新版本 SDK 通过在你尝试连接一个已连接的 server 时抛出错误来阻止这种情况。
之前的写法(在 SDK 1.26.0 下会失败)
JavaScript
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// INCORRECT: Global server instance
const server = new McpServer({
name: "Hello MCP Server",
version: "1.0.0",
});
server.tool("hello", "Returns a greeting", {}, async () => {
return {
content: [{ text: "Hello, World!", type: "text" }],
};
});
export default {
fetch: async (request, env, ctx) => {
// This will fail on second request with MCP SDK 1.26.0+
return createMcpHandler(server)(request, env, ctx);
},
};
Explain Code
TypeScript
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// INCORRECT: Global server instance
const server = new McpServer({
name: "Hello MCP Server",
version: "1.0.0",
});
server.tool("hello", "Returns a greeting", {}, async () => {
return {
content: [{ text: "Hello, World!", type: "text" }],
};
});
export default {
fetch: async (request: Request, env: Env, ctx: ExecutionContext) => {
// This will fail on second request with MCP SDK 1.26.0+
return createMcpHandler(server)(request, env, ctx);
},
} satisfies ExportedHandler<Env>;
Explain Code
修复后的写法(正确)
JavaScript
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// CORRECT: Factory function to create server instance
function createServer() {
const server = new McpServer({
name: "Hello MCP Server",
version: "1.0.0",
});
server.tool("hello", "Returns a greeting", {}, async () => {
return {
content: [{ text: "Hello, World!", type: "text" }],
};
});
return server;
}
export default {
fetch: async (request, env, ctx) => {
// Create new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
};
Explain Code
TypeScript
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// CORRECT: Factory function to create server instance
function createServer() {
const server = new McpServer({
name: "Hello MCP Server",
version: "1.0.0",
});
server.tool("hello", "Returns a greeting", {}, async () => {
return {
content: [{ text: "Hello, World!", type: "text" }],
};
});
return server;
}
export default {
fetch: async (request: Request, env: Env, ctx: ExecutionContext) => {
// Create new server instance per request
const server = createServer();
return createMcpHandler(server)(request, env, ctx);
},
} satisfies ExportedHandler<Env>;
Explain Code
直接使用原生 SDK transport 的用户
如果你直接使用原生 SDK transport(没有走 createMcpHandler),也必须按请求创建新的 transport 实例:
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
function createServer() {
const server = new McpServer({
name: "Hello MCP Server",
version: "1.0.0",
});
// Register tools...
return server;
}
export default {
async fetch(request) {
// Create new transport and server per request
const transport = new WebStandardStreamableHTTPServerTransport();
const server = createServer();
server.connect(transport);
return transport.handleRequest(request);
},
};
Explain Code
TypeScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
function createServer() {
const server = new McpServer({
name: "Hello MCP Server",
version: "1.0.0",
});
// Register tools...
return server;
}
export default {
async fetch(request: Request) {
// Create new transport and server per request
const transport = new WebStandardStreamableHTTPServerTransport();
const server = createServer();
server.connect(transport);
return transport.handleRequest(request);
},
} satisfies ExportedHandler<Env>;
Explain Code
WorkerTransport
WorkerTransport 类实现了 MCP Transport 接口,负责处理 HTTP 请求/响应循环、Server-Sent Events (SSE) 流式传输、会话管理与 CORS。
TypeScript
class WorkerTransport implements Transport {
sessionId?: string;
started: boolean;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;
constructor(options?: WorkerTransportOptions);
async handleRequest(
request: Request,
parsedBody?: unknown,
): Promise<Response>;
async send(
message: JSONRPCMessage,
options?: TransportSendOptions,
): Promise<void>;
async start(): Promise<void>;
async close(): Promise<void>;
}
Explain Code
构造函数选项
TypeScript
interface WorkerTransportOptions {
/**
* Function that generates a unique session ID.
* Called when a new session is initialized.
*/
sessionIdGenerator?: () => string;
/**
* Enable traditional Request/Response mode, disabling streaming.
* When true, responses are returned as JSON instead of SSE streams.
* @default false
*/
enableJsonResponse?: boolean;
/**
* Callback invoked when a session is initialized.
* Receives the generated or restored session ID.
*/
onsessioninitialized?: (sessionId: string) => void;
/**
* CORS configuration for cross-origin requests.
* Configures Access-Control-* headers.
*/
corsOptions?: CORSOptions;
/**
* Optional storage API for persisting transport state.
* Use this to store session state in Durable Object/Agent storage
* so it survives hibernation/restart.
*/
storage?: MCPStorageApi;
}
Explain Code
sessionIdGenerator
提供自定义的会话标识符。该会话标识符用于在 MCP 客户端中识别会话。
JavaScript
const transport = new WorkerTransport({
sessionIdGenerator: () => `user-${Date.now()}-${Math.random()}`,
});
TypeScript
const transport = new WorkerTransport({
sessionIdGenerator: () => `user-${Date.now()}-${Math.random()}`,
});
enableJsonResponse
禁用 SSE 流式传输,以标准 JSON 形式返回响应。
JavaScript
const transport = new WorkerTransport({
enableJsonResponse: true, // Disable streaming, return JSON responses
});
TypeScript
const transport = new WorkerTransport({
enableJsonResponse: true, // Disable streaming, return JSON responses
});
onsessioninitialized
会话初始化时触发的回调,无论是新建会话还是从存储恢复都会触发。
JavaScript
const transport = new WorkerTransport({
onsessioninitialized: (sessionId) => {
console.log(`MCP session initialized: ${sessionId}`);
},
});
TypeScript
const transport = new WorkerTransport({
onsessioninitialized: (sessionId) => {
console.log(`MCP session initialized: ${sessionId}`);
},
});
corsOptions
为跨域请求配置 CORS 头。
TypeScript
interface CORSOptions {
origin?: string;
methods?: string;
headers?: string;
maxAge?: number;
exposeHeaders?: string;
}
JavaScript
const transport = new WorkerTransport({
corsOptions: {
origin: "https://example.com",
methods: "GET, POST, OPTIONS",
headers: "Content-Type, Authorization",
maxAge: 86400,
},
});
TypeScript
const transport = new WorkerTransport({
corsOptions: {
origin: "https://example.com",
methods: "GET, POST, OPTIONS",
headers: "Content-Type, Authorization",
maxAge: 86400,
},
});
storage
持久化 transport 状态,使其在 Durable Object 休眠或重启时仍可恢复。
TypeScript
interface MCPStorageApi {
get(): Promise<TransportState | undefined> | TransportState | undefined;
set(state: TransportState): Promise<void> | void;
}
interface TransportState {
sessionId?: string;
initialized: boolean;
protocolVersion?: ProtocolVersion;
}
Explain Code
JavaScript
// Inside an Agent or Durable Object class method:
const transport = new WorkerTransport({
storage: {
get: async () => {
return await this.ctx.storage.get("mcp-state");
},
set: async (state) => {
await this.ctx.storage.put("mcp-state", state);
},
},
});
Explain Code
TypeScript
// Inside an Agent or Durable Object class method:
const transport = new WorkerTransport({
storage: {
get: async () => {
return await this.ctx.storage.get<TransportState>("mcp-state");
},
set: async (state) => {
await this.ctx.storage.put("mcp-state", state);
},
},
});
Explain Code
认证上下文
当通过 OAuth 认证 与 createMcpHandler 配合使用时,用户信息会通过 getMcpAuthContext() 提供给你的 MCP 工具。其底层使用 AsyncLocalStorage 将请求传递到工具处理器,从而保持认证上下文可用。
TypeScript
interface McpAuthContext {
props: Record<string, unknown>;
}
getMcpAuthContext
在 MCP 工具处理器内部获取当前的认证上下文。它返回的是 OAuth provider 填充的用户信息。注意:如果你使用的是 McpAgent,这些信息可以直接通过 this.props 访问。
TypeScript
import { getMcpAuthContext } from "agents/mcp";
function getMcpAuthContext(): McpAuthContext | undefined;
JavaScript
import { getMcpAuthContext } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
function createServer() {
const server = new McpServer({ name: "Auth Server", version: "1.0.0" });
server.tool("getProfile", "Get the current user's profile", {}, async () => {
const auth = getMcpAuthContext();
const username = auth?.props?.username;
const email = auth?.props?.email;
return {
content: [
{
type: "text",
text: `User: ${username ?? "anonymous"}, Email: ${email ?? "none"}`,
},
],
};
});
return server;
}
Explain Code
TypeScript
import { getMcpAuthContext } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
function createServer() {
const server = new McpServer({ name: "Auth Server", version: "1.0.0" });
server.tool("getProfile", "Get the current user's profile", {}, async () => {
const auth = getMcpAuthContext();
const username = auth?.props?.username as string | undefined;
const email = auth?.props?.email as string | undefined;
return {
content: [
{
type: "text",
text: `User: ${username ?? "anonymous"}, Email: ${email ?? "none"}`,
},
],
};
});
return server;
}
Explain Code
注意
完整介绍如何为 MCP 服务器配置 OAuth 认证,请参阅 MCP Authorization 文档。完整的“Worker 中的认证 MCP 服务器“示例请见 GitHub ↗。
错误处理
createMcpHandler 会自动捕获错误,并返回错误码为 -32603(Internal error)的 JSON-RPC 错误响应。
JavaScript
server.tool("riskyOperation", "An operation that might fail", {}, async () => {
if (Math.random() > 0.5) {
throw new Error("Random failure occurred");
}
return {
content: [{ type: "text", text: "Success!" }],
};
});
// Errors are automatically caught and returned as:
// {
// "jsonrpc": "2.0",
// "error": {
// "code": -32603,
// "message": "Random failure occurred"
// },
// "id": <request_id>
// }
Explain Code
TypeScript
server.tool("riskyOperation", "An operation that might fail", {}, async () => {
if (Math.random() > 0.5) {
throw new Error("Random failure occurred");
}
return {
content: [{ type: "text", text: "Success!" }],
};
});
// Errors are automatically caught and returned as:
// {
// "jsonrpc": "2.0",
// "error": {
// "code": -32603,
// "message": "Random failure occurred"
// },
// "id": <request_id>
// }
Explain Code
相关资源
Building MCP Servers 在 Cloudflare 上构建并部署 MCP 服务器。
MCP Tools 为你的 MCP 服务器添加工具。
MCP Authorization 通过 OAuth 认证用户。
McpAgent API 构建有状态的 MCP 服务器。