跳转到内容
搜索文档

createMcpHandler 参考

最后更新 查看 MarkdownAgent 设置

createMcpHandler 函数创建 fetch 处理程序来提供你的 MCP 服务器。当你需要运行在普通 Worker(无 Durable Object)中的无状态 MCP 服务器时使用。对于跨请求持久化状态的有状态 MCP 服务器,请改用 McpAgent 类。

它使用 MCP Transport 接口的实现 WorkerTransport,基于 Web 标准构建,符合 streamable-http 传输规范。

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>;

参数

返回值

签名 (request: Request, env: unknown, ctx: ExecutionContext) => Promise<Response> 的 Worker fetch 处理函数。

CreateMcpHandlerOptions

创建 MCP 处理程序的配置选项。

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;
}

选项

route

MCP 处理程序响应的 URL 路径。对其他路径的请求返回 404 响应。

默认值: "/mcp"

const handler = createMcpHandler(server, {
	route: "/api/mcp", // Only respond to requests at /api/mcp
});
const handler = createMcpHandler(server, {
	route: "/api/mcp", // Only respond to requests at /api/mcp
});

authContext

身份验证上下文对象,通过 getMcpAuthContext() 对 MCP 工具可用。

使用 @cloudflare/workers-oauth-providerOAuthProvider 时,身份验证上下文会自动填充 OAuth 流程中的信息。通常无需手动设置。

transport

自定义 WorkerTransport 实例。未提供时,每个请求都会创建新 transport。

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 });
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 });

无状态 MCP 服务器

许多 MCP 服务器是无状态的,意味着请求之间不维护任何会话状态。createMcpHandlerMcpAgent 类的轻量替代,可直接从 Worker 提供 MCP 服务器。在 GitHub 上查看完整示例

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);
	},
};
src/index.tsts
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>;

对此 MCP 服务器的每个请求都会创建新会话和服务器实例。服务器不在请求之间维护状态。这是实现 MCP 服务器的最简单方式。

有状态 MCP 服务器

对于需要在多个请求之间维护会话状态的有状态 MCP 服务器,你可以在 Agent 中直接使用带有 WorkerTransport 实例的 createMcpHandler 函数。如果你希望使用 elicitation 和 sampling 等高级客户端功能,这很有用。

提供带有持久存储的自定义 WorkerTransport。在 GitHub 上查看完整示例

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);
	}
}
src/index.tsts
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);
	}
}

此例将 sessionIdGenerator 定义为返回 Agent 名称作为 session ID。要在 Worker 处理程序中路由到正确 Agent,可使用 getAgentByName

import { getAgentByName } from "agents";

export default {
	async fetch(request, env, ctx) {
		// 从 header 提取 session ID 或生成新 ID
		const sessionId =
			request.headers.get("mcp-session-id") ?? crypto.randomUUID();

		// 按名称/session ID 获取 Agent 实例
		const agent = await getAgentByName(env.MyStatefulMcpAgent, sessionId);

		// 将 MCP 请求路由到 agent
		return await agent.onRequest(request);
	},
};
import { getAgentByName } from "agents";

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		// 从 header 提取 session ID 或生成新 ID
		const sessionId =
			request.headers.get("mcp-session-id") ?? crypto.randomUUID();

		// 按名称/session ID 获取 Agent 实例
		const agent = await getAgentByName(env.MyStatefulMcpAgent, sessionId);

		// 将 MCP 请求路由到 agent
		return await agent.onRequest(request);
	},
} satisfies ExportedHandler<Env>;

持久化 storage 下,transport 保留:

  • 重连时的 Session ID
  • 协议版本协商 state
  • 初始化状态

这使 MCP 客户端可在连接丢失后重连并恢复会话。

MCP SDK 1.26.0 迁移指南

MCP SDK 1.26.0 为无状态 MCP server 引入破坏性变更,修复使用共享 server 或 transport 实例时,一个客户端的响应可能泄漏给另一客户端的关键安全漏洞。

谁会受影响?

Server 类型 受影响? 所需操作
使用 Agent/Durable Object 的有状态 server 无需更改
使用 createMcpHandler 的无状态 server 每个请求创建新 McpServer
使用原始 SDK transport 的无状态 server 每个请求创建新 McpServer 与 transport

为何需要这样做?

在全局作用域声明 McpServer 实例的旧模式允许一个客户端的响应泄漏给另一个客户端。这是安全漏洞。新版 SDK 在尝试连接已连接的 server 时会抛出错误,从而防止此问题。

之前(在 SDK 1.26.0 中会失效)

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);
	},
};
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>;

之后(正确做法)

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);
	},
};
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>;

对于直接使用 SDK transport 的用户

若直接使用原始 SDK transport(非通过 createMcpHandler),每个请求也必须创建新 transport 实例:

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) {
		// 创建新 transport 与 server
		const transport = new WebStandardStreamableHTTPServerTransport();
		const server = createServer();
		server.connect(transport);
		return transport.handleRequest(request);
	},
};
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) {
		// 创建新 transport 与 server
		const transport = new WebStandardStreamableHTTPServerTransport();
		const server = createServer();
		server.connect(transport);
		return transport.handleRequest(request);
	},
} satisfies ExportedHandler<Env>;

WorkerTransport

WorkerTransport 类实现 MCP Transport 接口,处理 HTTP 请求/响应周期、Server-Sent Events (SSE) 流式传输、会话管理与 CORS。

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>;
}

构造函数选项

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;
}

sessionIdGenerator

提供自定义 session 标识符。该标识符用于在 MCP Client 中识别会话。

const transport = new WorkerTransport({
	sessionIdGenerator: () => `user-${Date.now()}-${Math.random()}`,
});
const transport = new WorkerTransport({
	sessionIdGenerator: () => `user-${Date.now()}-${Math.random()}`,
});

enableJsonResponse

禁用 SSE 流式传输,以标准 JSON 返回响应。

const transport = new WorkerTransport({
	enableJsonResponse: true, // 禁用流式传输,返回 JSON 响应
});
const transport = new WorkerTransport({
	enableJsonResponse: true, // 禁用流式传输,返回 JSON 响应
});

onsessioninitialized

会话初始化时触发的回调——无论是创建新会话还是从 storage 恢复。

const transport = new WorkerTransport({
	onsessioninitialized: (sessionId) => {
		console.log(`MCP session initialized: ${sessionId}`);
	},
});
const transport = new WorkerTransport({
	onsessioninitialized: (sessionId) => {
		console.log(`MCP session initialized: ${sessionId}`);
	},
});

corsOptions

配置跨域请求的 CORS 头。

interface CORSOptions {
	origin?: string;
	methods?: string;
	headers?: string;
	maxAge?: number;
	exposeHeaders?: string;
}
const transport = new WorkerTransport({
	corsOptions: {
		origin: "https://example.com",
		methods: "GET, POST, OPTIONS",
		headers: "Content-Type, Authorization",
		maxAge: 86400,
	},
});
const transport = new WorkerTransport({
	corsOptions: {
		origin: "https://example.com",
		methods: "GET, POST, OPTIONS",
		headers: "Content-Type, Authorization",
		maxAge: 86400,
	},
});

storage

持久化传输状态,以度过 Durable Object 休眠或重启。

interface MCPStorageApi {
	get(): Promise<TransportState | undefined> | TransportState | undefined;
	set(state: TransportState): Promise<void> | void;
}

interface TransportState {
	sessionId?: string;
	initialized: boolean;
	protocolVersion?: ProtocolVersion;
}
// 在 Agent 或 Durable Object 类方法内:
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);
		},
	},
});
// 在 Agent 或 Durable Object 类方法内:
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);
		},
	},
});

认证上下文

createMcpHandlerOAuth 身份验证 配合使用时,用户信息通过 getMcpAuthContext() 对 MCP tool 可用。底层使用 AsyncLocalStorage 将请求传递给 tool 处理程序,保持身份验证上下文可用。

interface McpAuthContext {
	props: Record<string, unknown>;
}

getMcpAuthContext

在 MCP tool 处理程序内获取当前身份验证上下文。返回 OAuth 提供商填充的用户信息。若使用 McpAgent,此信息可直接从 this.props 访问。

import { getMcpAuthContext } from "agents/mcp";

function getMcpAuthContext(): McpAuthContext | undefined;
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;
}
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;
}

错误处理

createMcpHandler 自动捕获错误并返回代码 -32603(Internal error)的 JSON-RPC 错误响应。

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!" }],
	};
});

// 错误自动捕获并返回为:
// {
//   "jsonrpc": "2.0",
//   "error": {
//     "code": -32603,
//     "message": "Random failure occurred"
//   },
//   "id": <request_id>
// }
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!" }],
	};
});

// 错误自动捕获并返回为:
// {
//   "jsonrpc": "2.0",
//   "error": {
//     "code": -32603,
//     "message": "Random failure occurred"
//   },
//   "id": <request_id>
// }

相关资源

这篇文档对您有帮助吗?