McpClient
把你的 agent 连接到外部 Model Context Protocol (MCP) 服务器,使用它们的工具、资源和提示词。这让你的 agent 能够通过标准化协议与 GitHub、Slack、数据库以及其他服务交互。
概述
MCP 客户端能力让你的 agent 可以:
- 连接外部 MCP 服务器 — GitHub、Slack、数据库、AI 服务
- 使用其工具 — 调用 MCP 服务器暴露的函数
- 访问资源 — 从 MCP 服务器读取数据
- 使用提示词 — 利用预先构建的提示词模板
注意
本页讨论作为客户端连接到 MCP 服务器。如果要创建自己的 MCP 服务器,请参阅创建 MCP 服务器。
快速开始
JavaScript
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");
}
}
TypeScript
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 服务器后,该服务器的所有工具都会自动可用。
添加 MCP 服务器
使用 addMcpServer() 连接到 MCP 服务器。对于不需要 OAuth 的服务器,无需任何额外选项:
JavaScript
// 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",
});
TypeScript
// 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",
});
传输方式选项
MCP 支持多种传输类型:
JavaScript
await this.addMcpServer("server", "https://mcp.example.com/mcp", {
transport: {
type: "streamable-http",
},
});
TypeScript
await this.addMcpServer("server", "https://mcp.example.com/mcp", {
transport: {
type: "streamable-http",
},
});
| 传输方式 | 说明 |
|---|---|
| auto | 根据服务器响应自动检测(默认) |
| streamable-http | 带流式传输的 HTTP |
| sse | Server-Sent Events — 旧版/兼容性传输 |
自定义请求头
对于位于身份验证后(例如 Cloudflare Access)或使用 bearer token 的服务器:
JavaScript
await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {
transport: {
headers: {
Authorization: "Bearer my-token",
"CF-Access-Client-Id": "...",
"CF-Access-Client-Secret": "...",
},
},
});
TypeScript
await this.addMcpServer("internal", "https://internal-mcp.example.com/mcp", {
transport: {
headers: {
Authorization: "Bearer my-token",
"CF-Access-Client-Id": "...",
"CF-Access-Client-Secret": "...",
},
},
});
URL 安全
为防止服务器端请求伪造(SSRF),建立连接前会先校验 MCP 服务器 URL。以下 URL 目标会被拦截:
- 私有/内部 IP 段(RFC 1918:
10.x、172.16-31.x、192.168.x) - 未指定地址(
0.0.0.0、[::]) - 链路本地地址(
169.254.x、fe80::) - IPv6 unique-local 地址(
fc00::/7) - 解析到私有段的 IPv4-mapped IPv6 地址(例如
[::ffff:10.0.0.1]) - 云元数据端点(
metadata.google.internal)
环回地址(localhost、127.x.x.x、[::1])在本地开发时允许使用。
生产环境下连接到内部服务时,请改用 RPC 传输,通过 Durable Object binding 而非 HTTP 通信。
返回值
addMcpServer() 返回连接状态:
ready— 服务器已连接,工具已发现authenticating— 服务器需要 OAuth;请把用户重定向到authUrl
OAuth 认证
许多 MCP 服务器需要 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)
在你的 agent 中处理 OAuth
JavaScript
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 });
}
}
TypeScript
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 });
}
}
OAuth 回调
回调 URL 会自动构造:
https://{host}/{agentsPrefix}/{agent-name}/{instance-name}/callback
例如:https://my-worker.workers.dev/agents/my-agent/default/callback
OAuth token 会安全地保存在 SQLite 中,跨 agent 重启依然有效。
在 OAuth 回调中保护实例名
当你使用 sendIdentityOnConnect: false 隐藏敏感的实例名(如会话 ID 或用户 ID)时,默认的 OAuth 回调 URL 会暴露实例名。要避免这个安全问题,你必须提供自定义的 callbackPath。
JavaScript
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 })
);
},
};
TypeScript
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 回调通过 state 查询参数(格式为 {serverId}:{stateValue})匹配,而不是通过 URL 路径。这意味着你的自定义 callbackPath 可以是任意路径,只要发往该路径的请求能被路由到正确的 agent 实例即可。
自定义 OAuth 回调处理
配置 OAuth 完成后的处理方式。默认情况下,认证成功会重定向到你的应用源,认证失败会显示一个 HTML 错误页。
JavaScript
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" },
});
},
});
}
}
TypeScript
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" },
});
},
});
}
}
使用 MCP 能力
连接建立后,即可访问服务器提供的能力:
获取可用工具
JavaScript
const state = this.getMcpServers();
// All tools from all connected servers
for (const tool of state.tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Description: ${tool.description}`);
}
TypeScript
const state = this.getMcpServers();
// All tools from all connected servers
for (const tool of state.tools) {
console.log(`Tool: ${tool.name}`);
console.log(` From server: ${tool.serverId}`);
console.log(` Description: ${tool.description}`);
}
资源与提示词
JavaScript
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}`);
}
TypeScript
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}`);
}
服务器状态
JavaScript
const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${server.name}: ${server.state}`);
// state: "ready" | "authenticating" | "connecting" | "connected" | "discovering" | "failed"
}
TypeScript
const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${server.name}: ${server.state}`);
// state: "ready" | "authenticating" | "connecting" | "connected" | "discovering" | "failed"
}
与 AI SDK 集成
要在 Vercel AI SDK 中使用 MCP 工具,使用 this.mcp.getAITools() 把 MCP 工具转换为 AI SDK 格式:
JavaScript
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);
}
}
TypeScript
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);
}
}
注意
getMcpServers().tools 返回原始的 MCP Tool 对象,适合用于检视。当你需要把工具传给 AI SDK 时,请使用 this.mcp.getAITools()。
管理服务器
移除服务器
JavaScript
await this.removeMcpServer(serverId);
TypeScript
await this.removeMcpServer(serverId);
这会断开与服务器的连接,并将其从存储中删除。
持久化
MCP 服务器在 agent 重启后依然保留:
- 服务器配置存储在 SQLite 中
- OAuth token 安全存储
- Agent 唤醒后会自动恢复连接
列出所有服务器
JavaScript
const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${id}: ${server.name} (${server.server_url})`);
}
TypeScript
const state = this.getMcpServers();
for (const [id, server] of Object.entries(state.servers)) {
console.log(`${id}: ${server.name} (${server.server_url})`);
}
客户端集成
已连接的客户端会通过 WebSocket 实时接收 MCP 更新:
JavaScript
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>
);
}
TypeScript
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>
);
}
API 参考
addMcpServer()
添加一个 MCP 服务器连接,把它的工具暴露给你的 agent。
当服务器名 和 URL 都与现有的活动连接匹配时,addMcpServer 是幂等的——会直接返回已有连接,不会重复创建。这样在 onStart() 中调用它时,无需担心重启后产生重复连接。
如果你用相同的名字但不同的 URL 调用 addMcpServer,则会创建一个新连接。两条连接都会保持活动,它们的工具会在 getAITools() 中合并。要替换某个服务器,请先调用 removeMcpServer(oldId)。
URL 在比较前会被规范化(尾部斜杠、默认端口、主机名大小写都会处理),因此 https://MCP.Example.com 和 https://mcp.example.com/ 会被视为同一个 URL。
TypeScript
// HTTP transport (Streamable HTTP, SSE)
async addMcpServer(
serverName: string,
url: string,
options?: {
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?: {
props?: Record<string, unknown>;
client?: ClientOptions;
retry?: RetryOptions;
}
): Promise<{ id: string; state: "ready" }>
参数(HTTP 传输)
serverName(string,必填)— MCP 服务器的展示名url(string,必填)— MCP 服务器端点的 URLoptions(object,可选)— 连接配置:callbackHost— OAuth 回调 URL 的主机。仅 OAuth 认证服务器需要。如果省略,会自动从入站请求或 WebSocket 连接 URI 推断——通常你不需要设置,除非你使用的自定义域名与 Worker 的主机名不同callbackPath— 自定义回调 URL 路径,绕开默认的/agents/{class}/{name}/callback构造。当sendIdentityOnConnect为false时必填,以避免泄漏实例名。设置后,回调 URL 会变成{callbackHost}/{callbackPath}。你必须通过getAgentByName把该路径路由到 agent 实例agentsPrefix— OAuth 回调路径的 URL 前缀。默认:"agents"。当提供了callbackPath时此项被忽略client— MCP 客户端配置项(传给@modelcontextprotocol/sdk的 Client 构造函数)。默认包含CfWorkerJsonSchemaValidator,用于按 JSON schema 验证工具参数transport— 传输层配置: *headers— 用于身份验证的自定义 HTTP 头 *type— 传输类型:"auto"(默认)、"streamable-http"或"sse"retry— 连接和重连尝试的重试选项。会被持久化,并在休眠或 OAuth 完成后恢复连接时使用。默认: 3 次尝试,500ms 基础延迟,5s 最大延迟。RetryOptions的详细说明参见 Retries
参数(RPC 传输)
serverName(string,必填)— MCP 服务器的展示名binding(DurableObjectNamespace,必填)—McpAgent类的 Durable Object bindingoptions(object,可选)— 连接配置:props— 传给McpAgent的onStart(props)的初始化数据。可用于传递用户上下文、配置或其他数据给 MCP 服务器实例client— MCP 客户端配置项retry— 连接的重试选项
RPC 传输通过 Durable Object binding 让你的 Agent 直接连接到 McpAgent,没有 HTTP 开销。配置 RPC 传输的详细信息参见 MCP Transport。
返回值
返回一个 Promise,根据连接状态解析为可辨识联合类型:
- 当
state为"authenticating"时:id(string)— 此服务器连接的唯一标识state("authenticating")— 服务器正在等待 OAuth 授权authUrl(string)— 用于用户认证的 OAuth 授权 URL
- 当
state为"ready"时:id(string)— 此服务器连接的唯一标识state("ready")— 服务器已完全连接并可用
removeMcpServer()
断开与 MCP 服务器的连接并清理相关资源。
TypeScript
async removeMcpServer(id: string): Promise<void>
参数
id(string,必填)—addMcpServer()返回的服务器连接 ID
getMcpServers()
获取所有 MCP 服务器连接的当前状态。
TypeScript
getMcpServers(): MCPServersState
返回值
TypeScript
type 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— 正在建立传输连接connected— 传输连接已建立discovering— 正在发现服务器能力(工具、资源、提示词)ready— 已完全连接并可用failed— 连接失败(详情见error字段)
error 字段在 state 为 "failed" 时包含错误消息。来自外部 OAuth 提供方的错误消息会被自动转义以防止 XSS 攻击,可以安全地直接显示在 UI 上。
configureOAuthCallback()
为需要认证的 MCP 服务器配置 OAuth 回调行为。该方法允许你自定义用户完成 OAuth 授权后的处理。
TypeScript
this.mcp.configureOAuthCallback(options: {
successRedirect?: string;
errorRedirect?: string;
customHandler?: () => Response | Promise<Response>;
}): void
参数
options(object,必填)— OAuth 回调配置:successRedirect(string,可选)— 认证成功后重定向到的 URLerrorRedirect(string,可选)— 认证失败后重定向到的 URL。错误信息会作为?error=<message>查询参数附加customHandler(function,可选)— 自定义处理器,完全控制回调响应。必须返回一个 Response
默认行为
未提供任何配置时:
- 成功: 重定向到你的应用源
- 失败: 显示一个带错误信息的 HTML 错误页
如果 OAuth 失败,连接状态会变为 "failed",错误信息会存放在 server.error 字段中,供你在 UI 中展示。
用法
在任何 OAuth 流程开始前,请在 onStart() 里完成配置:
JavaScript
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" },
});
},
});
}
}
TypeScript
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" },
});
},
});
}
}
自定义 OAuth provider
通过在 Agent 类上实现 createMcpOAuthProvider() 来覆盖连接 MCP 服务器时使用的默认 OAuth provider。这样可以使用预注册客户端凭证或 mTLS 等自定义认证策略,超越内置的动态客户端注册。
该覆盖会同时用于新连接(addMcpServer)和 Durable Object 重启后恢复的连接。
JavaScript
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,
};
},
};
}
}
TypeScript
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,通过 OAuth 2.0 动态客户端注册 ↗ 与 MCP 服务器对接。
自定义存储后端
如果你想保留内置的 OAuth 逻辑(CSRF state、PKCE、nonce 生成、token 管理),仅把 token 存储路由到不同的后端,可以引入 DurableObjectOAuthClientProvider 并传入自定义存储适配器:
JavaScript
import { Agent, DurableObjectOAuthClientProvider } from "agents";
export class MyAgent extends Agent {
createMcpOAuthProvider(callbackUrl) {
return new DurableObjectOAuthClientProvider(
myCustomStorage, // any DurableObjectStorage-compatible adapter
this.name,
callbackUrl,
);
}
}
TypeScript
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,
);
}
}
进阶: MCPClientManager
如果你需要更细粒度的控制,可以直接使用 this.mcp:
分步连接
JavaScript
// 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);
}
}
TypeScript
// 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);
}
}
事件订阅
JavaScript
// 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();
TypeScript
// 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();
注意
MCP 服务器列表广播(cf_agent_mcp_servers)会自动过滤掉 shouldSendProtocolMessages 返回 false 的连接。
生命周期方法
this.mcp.registerServer()
注册一个服务器但不立即建立连接。
TypeScript
async registerServer(
id: string,
options: {
url: string;
name: string;
callbackUrl: string;
clientOptions?: ClientOptions;
transportOptions?: TransportOptions;
}
): Promise<string>
this.mcp.connectToServer()
为先前注册的服务器建立连接。
TypeScript
async connectToServer(id: string): Promise<MCPConnectionResult>
type MCPConnectionResult =
| { state: "failed"; error: string }
| { state: "authenticating"; authUrl: string }
| { state: "connected" }
this.mcp.discoverIfConnected()
如果连接处于活动状态,检查服务器能力。
TypeScript
async discoverIfConnected(
serverId: string,
options?: { timeoutMs?: number }
): Promise<MCPDiscoverResult | undefined>
type MCPDiscoverResult = {
success: boolean;
state: MCPConnectionState;
error?: string;
}
this.mcp.waitForConnections()
等待所有进行中的 MCP 连接和发现操作完成。当你需要在 agent 从休眠唤醒后立即让 this.mcp.getAITools() 返回完整工具集时,这个方法很有用。
TypeScript
// Wait indefinitely
await this.mcp.waitForConnections();
// Wait with a timeout (milliseconds)
await this.mcp.waitForConnections({ timeout: 10_000 });
注意
AIChatAgent 会通过其 waitForMcpConnections 属性自动调用此方法(默认 { timeout: 10_000 })。只有在使用 Agent 配合 MCP,或者你想在 onChatMessage 内做更精细的控制时,才需要直接调用 waitForConnections()。
this.mcp.closeConnection()
关闭与某个服务器的连接,但保留其注册信息。
TypeScript
async closeConnection(id: string): Promise<void>
this.mcp.closeAllConnections()
关闭所有活动的服务器连接,但保留所有注册信息。
TypeScript
async closeAllConnections(): Promise<void>
this.mcp.getAITools()
以与 AI SDK 兼容的格式获取所有已发现的 MCP 工具。
TypeScript
getAITools(filter?: MCPServerFilter): ToolSet
工具会按服务器 ID 自动命名空间化,以避免多个 MCP 服务器暴露同名工具时发生冲突。
传入 MCPServerFilter 可以将返回的工具限定为已连接服务器的子集:
JavaScript
// 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" });
TypeScript
// 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 导入:
TypeScript
import type { MCPServerFilter } from "agents/mcp/client";
type MCPServerFilter = {
serverId?: string | string[];
serverName?: string | string[];
state?: MCPConnectionState | MCPConnectionState[];
};
所有指定的过滤条件之间为 AND 关系。listTools()、listPrompts()、listResources() 和 listResourceTemplates() 都接受相同的 filter 参数。
错误处理
使用错误检测工具来处理连接错误:
JavaScript
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;
}
}
}
TypeScript
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;
}
}
}
后续步骤
创建 MCP 服务器 构建你自己的 MCP 服务器。
Client SDK 在浏览器中通过 onMcpUpdate 连接。
存储与同步状态 了解 agent 的持久化机制。