跳转到内容
搜索文档

McpAgent

最后更新 查看 MarkdownAgent 设置

在 Cloudflare 上构建 MCP 服务器时,需扩展 Agents SDK 中的 McpAgent

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: "Demo", version: "1.0.0" });

	async init() {
		this.server.tool(
			"add",
			{ a: z.number(), b: z.number() },
			async ({ a, b }) => ({
				content: [{ type: "text", text: String(a + b) }],
			}),
		);
	}
}
src/index.tsts
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: "Demo", version: "1.0.0" });

	async init() {
		this.server.tool(
			"add",
			{ a: z.number(), b: z.number() },
			async ({ a, b }) => ({
				content: [{ type: "text", text: String(a + b) }],
			}),
		);
	}
}

这意味着 MCP 服务器的每个实例都有由 Durable Object 支持的持久状态,以及独立的 SQL 数据库

MCP 服务器不一定是 Agent。你可以构建无状态 MCP 服务器,仅使用 @modelcontextprotocol/sdk 包向服务器添加 工具

但若希望 MCP 服务器:

  • 记住先前的工具调用及其响应
  • 向 MCP 客户端提供游戏,并记住棋盘状态、先前走法与得分
  • 缓存先前外部 API 调用的状态,供后续工具调用复用
  • 执行 Agent 能做的一切,但允许 MCP 客户端与之通信

可使用下方 API 实现。

API 概览

属性/方法 描述
state 当前状态对象(已持久化)
initialState 实例启动时的默认状态
setState(state) 更新并持久化状态
onStateChanged(state) 状态变更时调用
sql 在嵌入式数据库上执行 SQL 查询
server 用于注册工具的 McpServer 实例
props OAuth 身份验证中的用户身份与令牌
elicitInput(options, context) 向用户请求结构化输入
McpAgent.serve(path, options) 创建 Worker 处理函数的静态方法

使用 McpAgent.serve() 部署

McpAgent.serve() 静态方法创建 Worker 处理程序,将请求路由到你的 MCP server:

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: "my-server", version: "1.0.0" });

	async init() {
		this.server.tool("square", { n: z.number() }, async ({ n }) => ({
			content: [{ type: "text", text: String(n * n) }],
		}));
	}
}

// 导出 Worker 处理程序
export default MyMCP.serve("/mcp");
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: "my-server", version: "1.0.0" });

	async init() {
		this.server.tool("square", { n: z.number() }, async ({ n }) => ({
			content: [{ type: "text", text: String(n * n) }],
		}));
	}
}

// 导出 Worker 处理程序
export default MyMCP.serve("/mcp");

这是部署 MCP server 的最简方式——约 15 行代码。serve() 方法自动处理 Streamable HTTP 传输。

配合 OAuth 认证

使用 OAuth Provider Library 时,将 MCP 服务器传入 apiHandlers

import { OAuthProvider } from "@cloudflare/workers-oauth-provider";

export default new OAuthProvider({
	apiHandlers: { "/mcp": MyMCP.serve("/mcp") },
	authorizeEndpoint: "/authorize",
	tokenEndpoint: "/token",
	clientRegistrationEndpoint: "/register",
	defaultHandler: AuthHandler,
});
import { OAuthProvider } from "@cloudflare/workers-oauth-provider";

export default new OAuthProvider({
	apiHandlers: { "/mcp": MyMCP.serve("/mcp") },
	authorizeEndpoint: "/authorize",
	tokenEndpoint: "/token",
	clientRegistrationEndpoint: "/register",
	defaultHandler: AuthHandler,
});

数据管辖

为符合 GDPR 与数据驻留要求,可指定 jurisdiction 以确保 MCP server 实例在特定区域运行:

// EU jurisdiction,符合 GDPR
export default MyMCP.serve("/mcp", { jurisdiction: "eu" });
// EU jurisdiction,符合 GDPR
export default MyMCP.serve("/mcp", { jurisdiction: "eu" });

配合 OAuth:

export default new OAuthProvider({
	apiHandlers: {
		"/mcp": MyMCP.serve("/mcp", { jurisdiction: "eu" }),
	},
	// ... 其他 OAuth 配置
});
export default new OAuthProvider({
	apiHandlers: {
		"/mcp": MyMCP.serve("/mcp", { jurisdiction: "eu" }),
	},
	// ... 其他 OAuth 配置
});

指定 jurisdiction: "eu" 时:

  • 所有 MCP 会话数据保留在 EU 内
  • tool 处理的用户数据保留在 EU 内
  • Durable Object 中存储的 state 保留在 EU 内

可用 jurisdiction 包括 "eu"(欧盟)与 "fedramp"(FedRAMP 合规位置)。更多选项请参阅 Durable Objects 数据位置

休眠支持

McpAgent 实例自动支持 WebSockets Hibernation,使有状态 MCP 服务器在非活跃期休眠同时保留状态。这意味着 Agent 仅在主动处理请求时消耗计算资源,在保持完整上下文与对话历史的同时优化成本。

休眠默认启用,无需额外配置。

流式可恢复性

McpAgent 的 Streamable HTTP 传输可度过 Cloudflare 边缘约 5 分钟的空闲流看门狗,因此在连接不稳定时进行中的工具调用不会丢失:

  • GET(独立监听流) — 配置 EventStore 时,空闲断开通过客户端以 Last-Event-ID 头重连恢复(无需保活)。无 EventStore 时使用注释帧保活(: keepalive,每 25 秒)保持长连接监听器存活。
  • POST(工具响应流) — 始终保活,进行中的工具调用可度过空闲看门狗。配置 EventStore 时 POST 流还可通过 Last-Event-ID 恢复;重连客户端重放遗漏事件直至最终响应。每个 POST 流在写入关闭帧时清除其事件。

DurableObjectEventStoreagents/mcp 导出,供在 Agent 或 Durable Object 内嵌 transport 的有状态 WorkerTransport 调用方使用:

import { DurableObjectEventStore } from "agents/mcp";

const eventStore = new DurableObjectEventStore(this.ctx.storage);
import { DurableObjectEventStore } from "agents/mcp";

const eventStore = new DurableObjectEventStore(this.ctx.storage);

传输配置请参阅 MCP Transport

认证与授权

McpAgent 类与 OAuth Provider Library 无缝集成,可用于身份验证与授权

用户向 MCP server 完成身份验证后,其身份信息与 token 通过 props 参数可用,你可:

  • 访问用户特定数据
  • 在操作前检查用户权限
  • 根据用户属性定制响应
  • 使用身份验证 token 代表用户向外部服务发起请求

状态同步 API

McpAgent 类提供对 Agent state API 的完整访问:

例如,以下代码实现记住计数器值、并在调用 add 工具时更新计数器的 MCP 服务器:

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: "Demo",
		version: "1.0.0",
	});

	initialState = {
		counter: 1,
	};

	async init() {
		this.server.resource(`counter`, `mcp://resource/counter`, (uri) => {
			return {
				contents: [{ uri: uri.href, text: String(this.state.counter) }],
			};
		});

		this.server.tool(
			"add",
			"Add to the counter, stored in the MCP",
			{ a: z.number() },
			async ({ a }) => {
				this.setState({ ...this.state, counter: this.state.counter + a });

				return {
					content: [
						{
							type: "text",
							text: String(`Added ${a}, total is now ${this.state.counter}`),
						},
					],
				};
			},
		);
	}

	onStateChanged(state) {
		console.log({ stateUpdate: state });
	}
}
src/index.tsts
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: "Demo",
		version: "1.0.0",
	});

	initialState: State = {
		counter: 1,
	};

	async init() {
		this.server.resource(`counter`, `mcp://resource/counter`, (uri) => {
			return {
				contents: [{ uri: uri.href, text: String(this.state.counter) }],
			};
		});

		this.server.tool(
			"add",
			"Add to the counter, stored in the MCP",
			{ a: z.number() },
			async ({ a }) => {
				this.setState({ ...this.state, counter: this.state.counter + a });

				return {
					content: [
						{
							type: "text",
							text: String(`Added ${a}, total is now ${this.state.counter}`),
						},
					],
				};
			},
		);
	}

	onStateChanged(state: State) {
		console.log({ stateUpdate: state });
	}
}

Elicitation(征询输入)(征询输入)

MCP elicitation 允许 server 在处理其他请求(如 tool call)时向用户请求输入。当前稳定 MCP 规范定义两种模式:

  • Form 模式 通过客户端收集结构化、非敏感数据。
  • URL 模式 将用户引导至带外交互,如第三方授权或支付。

server 发送前,客户端必须声明支持对应模式。

Form 模式

在工具处理函数中调用 this.server.server.elicitInput()。将 extra.requestId 作为 relatedRequestId 传入,使响应返回原始工具调用的流:

const result = await this.server.server.elicitInput(
	{
		mode: "form",
		message: "By how much do you want to increase the counter?",
		requestedSchema: {
			type: "object",
			properties: {
				amount: {
					type: "number",
					title: "Amount",
					minimum: 1,
					maximum: 100,
				},
			},
			required: ["amount"],
		},
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept" || !result.content) {
	return { content: [{ type: "text", text: "Counter unchanged." }] };
}

const amount = Number(result.content.amount);
const result = await this.server.server.elicitInput(
	{
		mode: "form",
		message: "By how much do you want to increase the counter?",
		requestedSchema: {
			type: "object",
			properties: {
				amount: {
					type: "number",
					title: "Amount",
					minimum: 1,
					maximum: 100,
				},
			},
			required: ["amount"],
		},
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept" || !result.content) {
	return { content: [{ type: "text", text: "Counter unchanged." }] };
}

const amount = Number(result.content.amount);

为向后兼容,表单请求可省略 mode: "form"。schema 支持带基本类型字段的扁平对象。请勿使用表单模式请求密码、API 密钥、访问令牌、支付凭证或其他机密。

URL 模式

URL 模式用于必须在 MCP 客户端外完成的交互。请求包含 message、URL 与唯一 elicitationId

const elicitationId = crypto.randomUUID();
const result = await this.server.server.elicitInput(
	{
		mode: "url",
		message: "Connect your account to continue.",
		url: `https://example.com/connect?elicitationId=${elicitationId}`,
		elicitationId,
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept") {
	return { content: [{ type: "text", text: "Connection cancelled." }] };
}

return {
	content: [
		{
			type: "text",
			text: "Connection page opened. Complete it in your browser.",
		},
	],
};
const elicitationId = crypto.randomUUID();
const result = await this.server.server.elicitInput(
	{
		mode: "url",
		message: "Connect your account to continue.",
		url: `https://example.com/connect?elicitationId=${elicitationId}`,
		elicitationId,
	},
	{ relatedRequestId: extra.requestId },
);

if (result.action !== "accept") {
	return { content: [{ type: "text", text: "Connection cancelled." }] };
}

return {
	content: [
		{
			type: "text",
			text: "Connection page opened. Complete it in your browser.",
		},
	],
};

URL 模式下,accept 表示用户同意打开 URL,不表示带外交互已完成。server 之后可发送 notifications/elicitation/complete,使用相同 elicitationId

请勿在 url 中放置机密、个人信息或预认证受保护资源 URL。生产 server 应使用 HTTPS。将每个请求绑定到已认证用户,并验证同一用户完成带外流程。

处理响应

两种模式均返回三种 action 之一:

操作 含义
accept 用户提交了表单或同意打开 URL。
decline 用户明确拒绝请求。
cancel 用户关闭请求但未明确选择。

已接受的表单响应包含与 requestedSchema 匹配的 content。URL 响应省略 contentdeclinecancel 响应通常也省略。

switch (result.action) {
	case "accept":
		// form 模式下,验证并处理 result.content。
		break;
	case "decline":
		return { content: [{ type: "text", text: "Request declined." }] };
	case "cancel":
		return { content: [{ type: "text", text: "Request dismissed." }] };
}
switch (result.action) {
	case "accept":
		// form 模式下,验证并处理 result.content。
		break;
	case "decline":
		return { content: [{ type: "text", text: "Request declined." }] };
	case "cancel":
		return { content: [{ type: "text", text: "Request dismissed." }] };
}

更多人机协同模式请参阅 Human-in-the-loop 模式

后续步骤

MCP 工具

设计并向 MCP 服务器添加工具。

授权

设置 OAuth 身份验证。

这篇文档对您有帮助吗?