将 Agent 连接到外部 Model Context Protocol (MCP) server,以使用其 tool、resource 与 prompt。这使 Agent 能通过标准化协议与 GitHub、Slack、数据库及其他服务交互。
MCP 客户端能力让你的 Agent 可以:
- 连接外部 MCP server — GitHub、Slack、数据库、AI 服务等
- 使用其 tool — 调用 MCP server 暴露的函数
- 访问 resource — 从 MCP server 读取数据
- 使用 prompt — 利用预构建的 prompt 模板
import { Agent } from "agents";
export class MyAgent extends Agent {
async onRequest(request) {
// Add an MCP server
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Server requires OAuth - redirect user to authorize
return Response.redirect(result.authUrl);
}
// Server is ready - tools are now available
const state = this.getMcpServers();
console.log(`Connected! ${state.tools.length} tools available`);
return new Response("MCP server connected");
}
}import { Agent } from "agents";
export class MyAgent extends Agent {
async onRequest(request: Request) {
// Add an MCP server
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Server requires OAuth - redirect user to authorize
return Response.redirect(result.authUrl);
}
// Server is ready - tools are now available
const state = this.getMcpServers();
console.log(`Connected! ${state.tools.length} tools available`);
return new Response("MCP server connected");
}
}连接持久化在 Agent 的 SQL 存储 中;Agent 连接到 MCP server 后,该 server 的所有 tool 会自动可用。
使用 addMcpServer() 连接 MCP server。非 OAuth server 无需选项:
// Non-OAuth server — no options required
await this.addMcpServer("notion", "https://mcp.notion.so/mcp");
// OAuth server — callbackHost is auto-derived from the incoming request,
// but you can set it explicitly if needed (e.g. custom domains)
await this.addMcpServer("github", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.workers.dev",
});// Non-OAuth server — no options required
await this.addMcpServer("notion", "https://mcp.notion.so/mcp");
// OAuth server — callbackHost is auto-derived from the incoming request,
// but you can set it explicitly if needed (e.g. custom domains)
await this.addMcpServer("github", "https://mcp.github.com/mcp", {
callbackHost: "https://my-worker.workers.dev",
});默认情况下,每个连接会分配生成的 nanoid(8) ID。对于 connector 式集成,传入 id 使 tool 以可读 key 而非 opaque 连接 ID 呈现。
await this.addMcpServer("GitHub", env.MCP_SESSION, {
id: "github",
props: { token: "..." },
});
// tools surface as `tool_github_<name>`await this.addMcpServer("GitHub", env.MCP_SESSION, {
id: "github",
props: { token: "..." },
});
// tools surface as `tool_github_<name>`提供后,此 id 会替换 storage、restore、listServers()、listTools()、getAITools() 与 OAuth state 中的生成值,作为 server ID。提供的 ID 会通过导出的 normalizeServerId helper 规范化,因此 "GitHub MCP!" 等值会变成 "github-mcp" — 保证 ID 可安全嵌入 AI SDK tool 名称与 storage key。
Stable ID 完全向后兼容 — 现有代码不会破坏。若对已在 auto-generated ID 下注册的 server 的 addMcpServer 调用添加 { id: "github" },SDK 会透明地将现有 storage 行、内存连接与 OAuth 相关 storage key 迁移到新 stable ID。无需先调用 removeMcpServer。addMcpServer 仅在相同 stable ID 已属于不同 (name, url) server 时抛出真正歧义的冲突。
MCP 支持多种 transport 类型:
await this.addMcpServer("server", "https://mcp.example.com/mcp", {
transport: {
type: "streamable-http",
},
});await this.addMcpServer("server", "https://mcp.example.com/mcp", {
transport: {
type: "streamable-http",
},
});| Transport | 描述 |
|---|---|
auto |
根据 server 响应自动检测(默认) |
streamable-http |
带流式传输的 HTTP |
sse |
Server-Sent Events — 旧版/兼容 transport |
对于需要身份验证(如 Cloudflare Access)或使用 bearer token 的 server:
await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {
transport: {
headers: {
Authorization: "Bearer my-token",
"CF-Access-Client-Id": "...",
"CF-Access-Client-Secret": "...",
},
},
});await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {
transport: {
headers: {
Authorization: "Bearer my-token",
"CF-Access-Client-Id": "...",
"CF-Access-Client-Secret": "...",
},
},
});连接前会验证 MCP server URL,以防止服务端请求伪造(SSRF)。以下 URL 目标会被阻止:
- 私有/内部 IP 范围(RFC 1918:
10.x、172.16-31.x、192.168.x) - 未指定地址(
0.0.0.0、[::]) - 链路本地地址(
169.254.x、fe80::) - IPv6 唯一本地地址(
fc00::/7) - 解析为私有范围的 IPv4 映射 IPv6 地址(例如
[::ffff:10.0.0.1]) - Cloud metadata 端点(
metadata.google.internal)
回环地址(localhost、127.x.x.x、[::1])在本地开发中允许。
生产环境中连接内部服务时,请使用带 Durable Object binding 的 RPC transport,而非 HTTP。
addMcpServer() 返回连接 state:
ready— server 已连接且 tool 已发现authenticating— server 需要 OAuth;将用户重定向到authUrl
许多 MCP server 需要 OAuth 认证。Agent 会自动处理 OAuth 流程。
sequenceDiagram
participant Client
participant Agent
participant MCPServer
Client->>Agent: addMcpServer(name, url)
Agent->>MCPServer: Connect
MCPServer-->>Agent: Requires OAuth
Agent-->>Client: state: authenticating, authUrl
Client->>MCPServer: User authorizes
MCPServer->>Agent: Callback with code
Agent->>MCPServer: Exchange for token
Agent-->>Client: onMcpUpdate (ready)
class MyAgent extends Agent {
async onRequest(request) {
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Redirect the user to the OAuth authorization page
return Response.redirect(result.authUrl);
}
return Response.json({ status: "connected", id: result.id });
}
}class MyAgent extends Agent {
async onRequest(request: Request) {
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
);
if (result.state === "authenticating") {
// Redirect the user to the OAuth authorization page
return Response.redirect(result.authUrl);
}
return Response.json({ status: "connected", id: result.id });
}
}回调 URL 会自动构造:
https://{host}/{agentsPrefix}/{agent-name}/{instance-name}/callback例如:https://my-worker.workers.dev/agents/my-agent/default/callback
OAuth token 安全存储在 SQLite 中,并在 Agent 重启后保留。
使用 sendIdentityOnConnect: false 隐藏敏感实例名称(如会话 ID 或用户 ID)时,默认 OAuth 回调 URL 会暴露实例名称。为防止此安全问题,必须提供自定义 callbackPath。
import { Agent, routeAgentRequest, getAgentByName } from "agents";
export class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
async onRequest(request) {
// callbackPath is required when sendIdentityOnConnect is false
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
{
callbackPath: "mcp-oauth-callback", // Custom path without instance name
},
);
if (result.state === "authenticating") {
return Response.redirect(result.authUrl);
}
return new Response("Connected!");
}
}
// Route the custom callback path to the agent
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Route custom MCP OAuth callback to agent instance
if (url.pathname.startsWith("/mcp-oauth-callback")) {
// Implement this to extract the instance name from your session/auth mechanism
const instanceName = await getInstanceNameFromSession(request);
const agent = await getAgentByName(env.SecureAgent, instanceName);
return agent.fetch(request);
}
// Standard agent routing
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
};import { Agent, routeAgentRequest, getAgentByName } from "agents";
export class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
async onRequest(request: Request) {
// callbackPath is required when sendIdentityOnConnect is false
const result = await this.addMcpServer(
"github",
"https://mcp.github.com/mcp",
{
callbackPath: "mcp-oauth-callback", // Custom path without instance name
},
);
if (result.state === "authenticating") {
return Response.redirect(result.authUrl);
}
return new Response("Connected!");
}
}
// Route the custom callback path to the agent
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Route custom MCP OAuth callback to agent instance
if (url.pathname.startsWith("/mcp-oauth-callback")) {
// Implement this to extract the instance name from your session/auth mechanism
const instanceName = await getInstanceNameFromSession(request);
const agent = await getAgentByName(env.SecureAgent, instanceName);
return agent.fetch(request);
}
// Standard agent routing
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;配置 OAuth 完成后的处理方式。默认情况下,成功身份验证会重定向到应用 origin,失败则显示 HTML 错误页。
export class MyAgent extends Agent {
onStart() {
this.mcp.configureOAuthCallback({
// Redirect after successful auth
successRedirect: "https://myapp.com/success",
// Redirect on error with error message in query string
errorRedirect: "https://myapp.com/error",
// Or use a custom handler
customHandler: () => {
// Close popup window after auth completes
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}export class MyAgent extends Agent {
onStart() {
this.mcp.configureOAuthCallback({
// Redirect after successful auth
successRedirect: "https://myapp.com/success",
// Redirect on error with error message in query string
errorRedirect: "https://myapp.com/error",
// Or use a custom handler
customHandler: () => {
// Close popup window after auth completes
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}连接后,访问 server 的能力:
使用 listTools() 检查原始 MCP 目录,而无需为 AI SDK model 调用准备 tool:
const tools = this.mcp.listTools();
for (const tool of tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Title: ${tool.title ?? tool.annotations?.title ?? tool.name}`);
console.log(` Description: ${tool.description}`);
}const tools = this.mcp.listTools();
for (const tool of tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Title: ${tool.title ?? tool.annotations?.title ?? tool.name}`);
console.log(` Description: ${tool.description}`);
}getMcpServers().tools 作为完整 MCP client state 的一部分返回相同的原始 tool 记录。两个 API 都不会转换 tool schema。
要在 AI SDK 中使用 MCP tool,请使用 this.mcp.getAITools(),它将 MCP tool 转换为 AI SDK 格式:
import { generateText } from "ai";
import { createWorkersAI } from "workers-ai-provider";
export class MyAgent extends Agent {
async onRequest(request) {
const workersai = createWorkersAI({ binding: this.env.AI });
const response = await generateText({
model: workersai("@cf/zai-org/glm-4.7-flash"),
prompt: "What's the weather in San Francisco?",
tools: this.mcp.getAITools(),
});
return new Response(response.text);
}
}import { generateText } from "ai";
import { createWorkersAI } from "workers-ai-provider";
export class MyAgent extends Agent<Env> {
async onRequest(request: Request) {
const workersai = createWorkersAI({ binding: this.env.AI });
const response = await generateText({
model: workersai("@cf/zai-org/glm-4.7-flash"),
prompt: "What's the weather in San Francisco?",
tools: this.mcp.getAITools(),
});
return new Response(response.text);
}
}const state = this.getMcpServers();
// Available resources
for (const resource of state.resources) {
console.log(`Resource: ${resource.name} (${resource.uri})`);
}
// Available prompts
for (const prompt of state.prompts) {
console.log(`Prompt: ${prompt.name}`);
}const state = this.getMcpServers();
// Available resources
for (const resource of state.resources) {
console.log(`Resource: ${resource.name} (${resource.uri})`);
}
// Available prompts
for (const prompt of state.prompts) {
console.log(`Prompt: ${prompt.name}`);
}MCP elicitation ↗ 允许 server 在处理其他请求(如 tool call)时向用户请求输入。当前稳定 MCP 规范定义 form 与 URL 模式。
在 onStart() 中为 Agent 支持的每种模式注册 handler:
import { Agent } from "agents";
class MyAgent extends Agent {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
forwardElicitationToBrowser(request, serverId) {
// Forward the request to your UI and resolve after the user responds.
// A complete implementation appears in Forward elicitation to a UI.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}import { Agent } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp";
class MyAgent extends Agent<Env> {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
private forwardElicitationToBrowser(
request: ElicitRequest,
serverId: string,
): Promise<ElicitResult> {
// Forward the request to your UI and resolve after the user responds.
// A complete implementation appears in Forward elicitation to a UI.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}serverId 标识发送请求的连接。用它告知用户哪个服务器在请求输入,并应用服务器特定策略。
在 MCP initialize 握手时,连接仅通告已配置处理程序的模式。仅表单处理程序时通告表单模式;仅 URL 处理程序时通告 URL 模式。无处理程序的连接不通告 elicitation 能力,服务器可使用其回退。
SDK 将通告的模式与每个服务器注册一并存储。Durable Object 休眠后恢复的连接可在重连时通告相同模式。回调函数保留在内存中,并在 onStart() 运行时重新挂接。
添加服务器时可显式收窄通告的模式:
await this.addMcpServer("portal", "https://portal.example.com/mcp", {
client: {
capabilities: {
elicitation: { form: {} },
},
},
});await this.addMcpServer("portal", "https://portal.example.com/mcp", {
client: {
capabilities: {
elicitation: { form: {} },
},
},
});显式 client.capabilities.elicitation 值优先于处理程序推导的模式,并与服务器注册一并持久化。不要通告没有匹配处理程序的模式。若服务器发送该模式,连接会返回错误,因为无法处理请求。
Form 模式在 client 内收集结构化、非敏感数据。请求在 requestedSchema 中包含受限 JSON Schema。若用户提交表单,返回带匹配 content 的 action: "accept":
this.mcp.configureElicitationHandlers({
form: async (request) => {
const content = await showFormToUser(request.params.requestedSchema);
return content ? { action: "accept", content } : { action: "cancel" };
},
});this.mcp.configureElicitationHandlers({
form: async (request) => {
const content = await showFormToUser(request.params.requestedSchema);
return content ? { action: "accept", content } : { action: "cancel" };
},
});允许用户在提交前审阅并编辑值。根据 requestedSchema 验证已接受 content。不要使用 form 模式请求密码、API 密钥、access token、支付凭证或其他机密。
URL 模式要求用户打开外部页面。用于可能收集机密的带外交互,如第三方授权或支付。将 URL 保留在专用 elicitation 路径中,不要放入 model 可见消息或 tool result 文本。
URL handler 应:
- 显示哪个 MCP server 发送了请求。
- 显示请求 message、目标 host 与完整 URL。
- 打开 URL 前请求同意。
- 在 Agent 与 model 无法检查的 browser 上下文中打开页面。
- 同意后返回不带
content的action: "accept"。 - 提供独立的 decline 与 cancel 控件。
不要 prefetch URL 或其 metadata。将 URL 视为不可信输入。生产 server 应发送 HTTPS URL。
URL 模式下,accept 表示用户同意打开 URL,不表示带外交互已完成。server 之后可发送带请求 elicitationId 的 notifications/elicitation/complete。
两种模式均支持三种 action:
| Action | 含义 |
|---|---|
accept |
用户提交了表单或同意打开 URL。 |
decline |
用户明确拒绝请求。 |
cancel |
用户关闭请求但未明确选择。 |
仅对已接受的 form 响应包含 content。URL、decline 与 cancel 响应省略。
处理程序返回 promise,但响应通常来自浏览器。将请求广播到已连接客户端,然后通过 @callable 方法兑现 promise:
import { Agent, callable } from "agents";
class MyAgent extends Agent {
pendingElicitations = new Map();
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) => this.forward(request, serverId),
url: (request, serverId) => this.forward(request, serverId),
});
}
forward(request, serverId) {
const id = crypto.randomUUID();
const result = new Promise((resolve) => {
const timeout = setTimeout(() => {
if (this.pendingElicitations.delete(id)) {
resolve({ action: "cancel" });
}
}, 55_000);
this.pendingElicitations.set(id, { resolve, timeout });
});
this.broadcast(
JSON.stringify({
type: "mcp-elicitation",
id,
serverId,
params: request.params,
}),
);
return result;
}
@callable()
respondToElicitation(id, result) {
const pending = this.pendingElicitations.get(id);
if (!pending) return;
this.pendingElicitations.delete(id);
clearTimeout(pending.timeout);
pending.resolve(result);
}
}import { Agent, callable } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp";
type PendingResolver = {
resolve: (result: ElicitResult) => void;
timeout: ReturnType<typeof setTimeout>;
};
class MyAgent extends Agent<Env> {
private pendingElicitations = new Map<string, PendingResolver>();
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) => this.forward(request, serverId),
url: (request, serverId) => this.forward(request, serverId),
});
}
private forward(
request: ElicitRequest,
serverId: string,
): Promise<ElicitResult> {
const id = crypto.randomUUID();
const result = new Promise<ElicitResult>((resolve) => {
const timeout = setTimeout(() => {
if (this.pendingElicitations.delete(id)) {
resolve({ action: "cancel" });
}
}, 55_000);
this.pendingElicitations.set(id, { resolve, timeout });
});
this.broadcast(
JSON.stringify({
type: "mcp-elicitation",
id,
serverId,
params: request.params,
}),
);
return result;
}
@callable()
respondToElicitation(id: string, result: ElicitResult) {
const pending = this.pendingElicitations.get(id);
if (!pending) return;
this.pendingElicitations.delete(id);
clearTimeout(pending.timeout);
pending.resolve(result);
}
}示例使用 55 秒 timeout,因为 MCP SDK 请求默认 60 秒。若 client 调用设置了更长 request timeout,请调整此 timeout 使其先完成。
browser 实现请参阅 mcp-client 示例 ↗。mcp-elicitation 示例 ↗ 是发送两种模式的 server。
从 MCP server 发送 elicitation 请求请参阅 elicitInput。
MCP server 注册在 Agent 重启后保留。SDK 在 SQLite 中存储 server 配置、安全存储 OAuth token,并在 Agent 唤醒时恢复连接。
const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${id}: ${server.name} (${server.server_url})`);
}const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${id}: ${server.name} (${server.server_url})`);
}使用 server ID 检查单个连接:
const state = this.getMcpServers();
const server = state.servers[serverId];
if (server) {
console.log(`${server.name}: ${server.state}`);
// state: "ready" | "authenticating" | "connecting" | "connected" | "discovering" | "failed"
}const state = this.getMcpServers();
const server = state.servers[serverId];
if (server) {
console.log(`${server.name}: ${server.state}`);
// state: "ready" | "authenticating" | "connecting" | "connected" | "discovering" | "failed"
}await this.removeMcpServer(serverId);await this.removeMcpServer(serverId);这会断开与 server 的连接并将其从 storage 中移除。
已连接 client 通过 WebSocket 接收实时 MCP 更新:
import { useAgent } from "agents/react";
import { useState } from "react";
function Dashboard() {
const [tools, setTools] = useState([]);
const [servers, setServers] = useState({});
const agent = useAgent({
agent: "MyAgent",
onMcpUpdate: (mcpState) => {
setTools(mcpState.tools);
setServers(mcpState.servers);
},
});
return (
<div>
<h2>Connected Servers</h2>
{Object.entries(servers).map(([id, server]) => (
<div key={id}>
{server.name}: {server.state}
</div>
))}
<h2>Available Tools ({tools.length})</h2>
{tools.map((tool) => (
<div key={`${tool.serverId}-${tool.name}`}>{tool.name}</div>
))}
</div>
);
}import { useAgent } from "agents/react";
import { useState } from "react";
function Dashboard() {
const [tools, setTools] = useState([]);
const [servers, setServers] = useState({});
const agent = useAgent({
agent: "MyAgent",
onMcpUpdate: (mcpState) => {
setTools(mcpState.tools);
setServers(mcpState.servers);
},
});
return (
<div>
<h2>Connected Servers</h2>
{Object.entries(servers).map(([id, server]) => (
<div key={id}>
{server.name}: {server.state}
</div>
))}
<h2>Available Tools ({tools.length})</h2>
{tools.map((tool) => (
<div key={`${tool.serverId}-${tool.name}`}>{tool.name}</div>
))}
</div>
);
}添加与 MCP server 的连接,使其 tool 对 agent 可用。
当 server 名称与 URL 均匹配现有活跃连接时,调用 addMcpServer 是幂等的 — 返回现有连接而不创建重复项。因此在 onStart() 中调用是安全的,重启时无需担心重复连接。
若以相同名称但不同 URL 调用 addMcpServer,会创建新连接。两个连接保持活跃,其 tool 在 getAITools() 中合并。要替换 server,先调用 removeMcpServer(oldId)。
比较前 URL 会规范化(尾随斜杠、默认端口与 hostname 大小写),因此 https://MCP.Example.com 与 https://mcp.example.com/ 视为相同 URL。
// HTTP transport (Streamable HTTP, SSE)
async addMcpServer(
serverName: string,
url: string,
options?: {
id?: string;
callbackHost?: string;
callbackPath?: string;
agentsPrefix?: string;
client?: ClientOptions;
transport?: {
headers?: HeadersInit;
type?: "sse" | "streamable-http" | "auto";
};
retry?: RetryOptions;
}
): Promise<
| { id: string; state: "authenticating"; authUrl: string }
| { id: string; state: "ready" }
>
// RPC transport (Durable Object binding — no HTTP overhead)
async addMcpServer(
serverName: string,
binding: DurableObjectNamespace,
options?: {
id?: string;
props?: Record<string, unknown>;
client?: ClientOptions;
retry?: RetryOptions;
}
): Promise<{ id: string; state: "ready" }>serverName(string,必需)— MCP server 的显示名称url(string,必需)— MCP server 端点 URLoptions(object,可选)— 连接配置:id— 可选的稳定、调用方提供的 server ID,用于 connector 式集成。提供后,会替换 storage、listServers()、listTools()、getAITools()(tool key 变为可读,例如tool_github_create_pull_request)与 OAuth state 中生成的nanoid(8)。请参阅稳定的 server IDcallbackHost— OAuth 回调 URL 的主机。仅 OAuth 认证的服务器需要。省略时自动从入站请求或 WebSocket 连接 URI 推导 — 通常无需设置,除非使用与 Worker 主机名不同的自定义域callbackPath— 绕过默认/agents/{class}/{name}/callback构造的自定义回调 URL 路径。当sendIdentityOnConnect为false时必需,以防泄漏实例名称。设置后回调 URL 为{callbackHost}/{callbackPath}。必须通过getAgentByName将此路径路由到 agent 实例agentsPrefix— OAuth 回调路径的 URL 前缀。默认:"agents"。提供callbackPath时忽略client— MCP 客户端配置选项(传给@modelcontextprotocol/sdkClient 构造函数)。默认包含CfWorkerJsonSchemaValidator,用于根据 JSON schema 验证工具参数transport— 传输层配置:headers— 用于身份验证的自定义 HTTP 标头type— 传输类型:"auto"(默认)、"streamable-http"或"sse"
retry— 连接与重连尝试的重试选项。持久化并在休眠或 OAuth 完成后恢复连接时使用。默认:3 次尝试,500ms 基础延迟,5s 最大延迟。RetryOptions详情请参阅 重试。
serverName(string,必需)— MCP server 的显示名称binding(DurableObjectNamespace,必需)—McpAgent类的 Durable Object 绑定options(object,可选)— 连接配置:id— 可选的稳定、调用方提供的 server ID。请参阅稳定的 server IDprops— 传给McpAgent的onStart(props)的初始化数据。用于向 MCP server instance 传递用户 context、配置或其他数据client— MCP client 配置选项retry— 连接的重试选项
RPC transport 通过 Durable Object 绑定将 Agent 直接连接到 McpAgent,无 HTTP 开销。RPC transport 配置详情请参阅 MCP Transport。
根据连接 state 解析为 discriminated union 的 Promise:
-
当
state为"authenticating"时:id(string)— 此 server 连接的唯一标识符state("authenticating")— server 等待 OAuth 授权authUrl(string)— 用户身份验证的 OAuth 授权 URL
-
当
state为"ready"时:id(string)— 此 server 连接的唯一标识符state("ready")— server 已完全连接并可运行
断开与 MCP server 的连接并清理其资源。
async removeMcpServer(id: string): Promise<void>id(string,必需)—addMcpServer()返回的 server 连接 ID
获取所有 MCP server 连接的当前 state。
getMcpServers(): MCPServersStatetype MCPServersState = {
servers: Record<
string,
{
name: string;
server_url: string;
auth_url: string | null;
state:
| "authenticating"
| "connecting"
| "connected"
| "discovering"
| "ready"
| "failed";
capabilities: ServerCapabilities | null;
instructions: string | null;
error: string | null;
}
>;
tools: Array<Tool & { serverId: string }>;
prompts: Array<Prompt & { serverId: string }>;
resources: Array<Resource & { serverId: string }>;
resourceTemplates: Array<ResourceTemplate & { serverId: string }>;
};state 字段表示连接生命周期:
authenticating— 等待 OAuth 授权完成connecting— 建立 transport 连接connected— transport 连接已建立discovering— 发现 server 能力(tool、resource、prompt)ready— 已完全连接并可运行failed— 连接失败(详情见error字段)
当 state 为 "failed" 时,error 字段包含错误消息。外部 OAuth 提供商的错误消息会自动转义以防 XSS 攻击,可安全直接在 UI 中显示。
配置需要身份验证的 MCP 服务器的 OAuth 回调行为。此方法允许自定义用户完成 OAuth 授权后的行为。
this.mcp.configureOAuthCallback(options: {
successRedirect?: string;
errorRedirect?: string;
customHandler?: () => Response | Promise<Response>;
}): voidoptions(object,必需)— OAuth 回调配置:successRedirect(string,可选)— 身份验证成功后重定向的 URLerrorRedirect(string,可选)— 身份验证失败后重定向的 URL。错误消息作为?error=<message>查询参数附加customHandler(function,可选)— 完全控制回调响应的自定义处理程序。必须返回 Response
未提供配置时:
- 成功:重定向到应用 origin
- 失败:显示带错误消息的 HTML 错误页
OAuth 失败时,连接 state 变为 "failed",错误消息存储在 server.error 字段,供 UI 显示。
在任何 OAuth 流程开始前于 onStart() 中配置:
export class MyAgent extends Agent {
onStart() {
// Option 1: Simple redirects
this.mcp.configureOAuthCallback({
successRedirect: "/dashboard",
errorRedirect: "/auth-error",
});
// Option 2: Custom handler (e.g., for popup windows)
this.mcp.configureOAuthCallback({
customHandler: () => {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}export class MyAgent extends Agent {
onStart() {
// Option 1: Simple redirects
this.mcp.configureOAuthCallback({
successRedirect: "/dashboard",
errorRedirect: "/auth-error",
});
// Option 2: Custom handler (e.g., for popup windows)
this.mcp.configureOAuthCallback({
customHandler: () => {
return new Response("<script>window.close();</script>", {
headers: { "content-type": "text/html" },
});
},
});
}
}为 server 发起的 elicitation/create 请求配置 handler。为 Agent 支持的每种 elicitation 模式添加 handler。
this.mcp.configureElicitationHandlers(handlers?: {
form?: (
request: ElicitRequest,
serverId: string,
) => Promise<ElicitResult>;
url?: (
request: ElicitRequest,
serverId: string,
) => Promise<ElicitResult>;
}): voidhandlers(object,可选)— 按模式键控的 elicitation handler:form(function,可选)— 处理 form 模式请求,用于结构化、非敏感输入。url(function,可选)— 处理 URL 模式请求,用于带外交互。
request(ElicitRequest)— MCP elicitation 请求。检查request.params.mode获取模式特定字段。serverId(string)— 发送请求的 MCP server 连接 ID。
每个 handler 返回包含 ElicitResult 的 promise。返回 accept、decline 或 cancel。已接受的 form 响应包含与 requestedSchema 匹配的 content。URL 响应省略 content。
传入 undefined 会清除所有已配置的 handler。
客户端在 MCP initialize 握手期间仅通告已配置处理程序的模式。处理程序变更立即应用于实时连接,但服务器在连接重连后才会收到更新的通告模式。
SDK 将处理程序推导的模式与每个 MCP 服务器注册一并存储。Durable Object 休眠后恢复的连接会通告这些模式,回调在 onStart() 运行时重新挂接。
在 onStart() 中配置 handler:
import { Agent } from "agents";
export class MyAgent extends Agent {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
forwardElicitationToBrowser(request, serverId) {
// Forward the request to your UI and resolve after the user responds.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}import { Agent } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp";
export class MyAgent extends Agent<Env> {
onStart() {
this.mcp.configureElicitationHandlers({
form: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
url: (request, serverId) =>
this.forwardElicitationToBrowser(request, serverId),
});
}
private forwardElicitationToBrowser(
request: ElicitRequest,
serverId: string,
): Promise<ElicitResult> {
// Forward the request to your UI and resolve after the user responds.
throw new Error(
`Implement elicitation for ${serverId}: ${request.params.message}`,
);
}
}完整 browser 转发模式与模式特定要求请参阅 Elicitation(征询输入)。
通过在 Agent 类上实现 createMcpOAuthProvider() 覆盖连接 MCP server 时使用的默认 OAuth provider。这支持内置动态 client 注册之外的自定义身份验证策略,如预注册 client 凭证或 mTLS。
覆盖用于新连接(addMcpServer)与 Durable Object 重启后恢复的连接。
import { Agent } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl) {
const env = this.env;
return {
get redirectUrl() {
return callbackUrl;
},
get clientMetadata() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
redirect_uris: [callbackUrl],
};
},
clientInformation() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
};
},
};
}
}import { Agent } from "agents";
import type { AgentMcpOAuthProvider } from "agents";
export class MyAgent extends Agent<Env> {
createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider {
const env = this.env;
return {
get redirectUrl() {
return callbackUrl;
},
get clientMetadata() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
redirect_uris: [callbackUrl],
};
},
clientInformation() {
return {
client_id: env.MCP_CLIENT_ID,
client_secret: env.MCP_CLIENT_SECRET,
};
},
};
}
}若不覆盖此方法,agent 使用默认 provider,与 MCP server 执行 OAuth 2.0 Dynamic Client Registration ↗。
保留内置 OAuth 逻辑(CSRF state、PKCE、nonce 生成、token 管理),但将 token storage 路由到不同后端时,导入 DurableObjectOAuthClientProvider 并传入自有 storage adapter:
import { Agent, DurableObjectOAuthClientProvider } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl) {
return new DurableObjectOAuthClientProvider(
myCustomStorage, // any DurableObjectStorage-compatible adapter
this.name,
callbackUrl,
);
}
}import { Agent, DurableObjectOAuthClientProvider } from "agents";
import type { AgentMcpOAuthProvider } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider {
return new DurableObjectOAuthClientProvider(
myCustomStorage, // any DurableObjectStorage-compatible adapter
this.name,
callbackUrl,
);
}
}需要细粒度控制时,直接使用 this.mcp:
// 1. Register the server (saves to storage and creates in-memory connection)
const id = "my-server";
await this.mcp.registerServer(id, {
url: "https://mcp.example.com/mcp",
name: "My Server",
callbackUrl: "https://my-worker.workers.dev/agents/my-agent/default/callback",
transport: { type: "auto" },
});
// 2. Connect (initializes transport, handles OAuth if needed)
const connectResult = await this.mcp.connectToServer(id);
if (connectResult.state === "failed") {
console.error("Connection failed:", connectResult.error);
return;
}
if (connectResult.state === "authenticating") {
console.log("OAuth required:", connectResult.authUrl);
return;
}
// 3. Discover capabilities (transitions from "connected" to "ready")
if (connectResult.state === "connected") {
const discoverResult = await this.mcp.discoverIfConnected(id);
if (!discoverResult?.success) {
console.error("Discovery failed:", discoverResult?.error);
}
}// 1. Register the server (saves to storage and creates in-memory connection)
const id = "my-server";
await this.mcp.registerServer(id, {
url: "https://mcp.example.com/mcp",
name: "My Server",
callbackUrl: "https://my-worker.workers.dev/agents/my-agent/default/callback",
transport: { type: "auto" },
});
// 2. Connect (initializes transport, handles OAuth if needed)
const connectResult = await this.mcp.connectToServer(id);
if (connectResult.state === "failed") {
console.error("Connection failed:", connectResult.error);
return;
}
if (connectResult.state === "authenticating") {
console.log("OAuth required:", connectResult.authUrl);
return;
}
// 3. Discover capabilities (transitions from "connected" to "ready")
if (connectResult.state === "connected") {
const discoverResult = await this.mcp.discoverIfConnected(id);
if (!discoverResult?.success) {
console.error("Discovery failed:", discoverResult?.error);
}
}// Listen for state changes (onServerStateChanged is an Event<void>)
const disposable = this.mcp.onServerStateChanged(() => {
console.log("MCP server state changed");
this.broadcastMcpServers(); // Notify connected clients
});
// Clean up the subscription when no longer needed
// disposable.dispose();// Listen for state changes (onServerStateChanged is an Event<void>)
const disposable = this.mcp.onServerStateChanged(() => {
console.log("MCP server state changed");
this.broadcastMcpServers(); // Notify connected clients
});
// Clean up the subscription when no longer needed
// disposable.dispose();注册 server 但不立即连接。
async registerServer(
id: string,
options: {
url: string;
name: string;
callbackUrl: string;
clientOptions?: ClientOptions;
transportOptions?: TransportOptions;
}
): Promise<string>建立与先前已注册 server 的连接。
async connectToServer(id: string): Promise<MCPConnectionResult>
type MCPConnectionResult =
| { state: "failed"; error: string }
| { state: "authenticating"; authUrl: string }
| { state: "connected" }若连接处于活跃状态,检查 server 能力。
async discoverIfConnected(
serverId: string,
options?: { timeoutMs?: number }
): Promise<MCPDiscoverResult | undefined>
type MCPDiscoverResult = {
success: boolean;
state: MCPConnectionState;
error?: string;
}等待所有进行中的 MCP 连接与发现操作结算。当 agent 从休眠唤醒后需要 this.mcp.getAITools() 立即返回完整工具集合时很有用。
// Wait indefinitely
await this.mcp.waitForConnections();
// Wait with a timeout (milliseconds)
await this.mcp.waitForConnections({ timeout: 10_000 });关闭与特定 server 的连接,但保留其注册。
async closeConnection(id: string): Promise<void>关闭所有活跃 server 连接,但保留注册。
async closeAllConnections(): Promise<void>获取原始 MCP tool 记录,不将其 schema 转换为 Zod。
listTools(filter?: MCPServerFilter): Array<Tool & { serverId: string }>使用此方法进行目录发现与检查。传入 MCPServerFilter 将返回的 tool 限制到特定连接。
以 AI SDK 兼容格式获取所有已发现的 MCP tool。
getAITools(filter?: MCPServerFilter): ToolSet多个 MCP 服务器暴露同名工具时,工具会按服务器 ID 自动命名空间化,以防冲突。
getAITools() 在每个实时连接上复用当前目录的已转换 schema。发现替换目录或实时连接变更后会再次转换 schema。每次调用返回新的工具记录与 execute 函数。仅需原始目录时使用 this.mcp.listTools()。
传入 MCPServerFilter 将返回的 tool 限定到已连接 server 的子集:
// Tools from a specific server only
const githubTools = this.mcp.getAITools({ serverId: "github" });
// Tools from multiple servers
const tools = this.mcp.getAITools({ serverId: ["github", "notion"] });
// Tools from servers matching a name
const tools = this.mcp.getAITools({ serverName: "GitHub" });
// Only tools from servers that are ready
const tools = this.mcp.getAITools({ state: "ready" });// Tools from a specific server only
const githubTools = this.mcp.getAITools({ serverId: "github" });
// Tools from multiple servers
const tools = this.mcp.getAITools({ serverId: ["github", "notion"] });
// Tools from servers matching a name
const tools = this.mcp.getAITools({ serverName: "GitHub" });
// Only tools from servers that are ready
const tools = this.mcp.getAITools({ state: "ready" });filter 类型可从 agents/mcp/client 获取:
import type { MCPServerFilter } from "agents/mcp/client";
type MCPServerFilter = {
serverId?: string | string[];
serverName?: string | string[];
state?: MCPConnectionState | MCPConnectionState[];
};所有指定的 filter 条件以 AND 组合。listTools()、listPrompts()、listResources() 与 listResourceTemplates() 接受相同的 filter 参数。
使用错误检测工具处理连接错误:
import { isUnauthorized, isTransportNotImplemented } from "agents";
export class MyAgent extends Agent {
async onRequest(request) {
try {
await this.addMcpServer("Server", "https://mcp.example.com/mcp");
} catch (error) {
if (isUnauthorized(error)) {
return new Response("Authentication required", { status: 401 });
} else if (isTransportNotImplemented(error)) {
return new Response("Transport not supported", { status: 400 });
}
throw error;
}
}
}import { isUnauthorized, isTransportNotImplemented } from "agents";
export class MyAgent extends Agent {
async onRequest(request: Request) {
try {
await this.addMcpServer("Server", "https://mcp.example.com/mcp");
} catch (error) {
if (isUnauthorized(error)) {
return new Response("Authentication required", { status: 401 });
} else if (isTransportNotImplemented(error)) {
return new Response("Transport not supported", { status: 400 });
}
throw error;
}
}
}