Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
sseServer-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.x172.16-31.x192.168.x)
  • 未指定地址(0.0.0.0[::])
  • 链路本地地址(169.254.xfe80::)
  • IPv6 unique-local 地址(fc00::/7)
  • 解析到私有段的 IPv4-mapped IPv6 地址(例如 [::ffff:10.0.0.1])
  • 云元数据端点(metadata.google.internal)

环回地址(localhost127.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.comhttps://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 服务器端点的 URL
  • options(object,可选)— 连接配置:
    • callbackHost — OAuth 回调 URL 的主机。仅 OAuth 认证服务器需要。如果省略,会自动从入站请求或 WebSocket 连接 URI 推断——通常你不需要设置,除非你使用的自定义域名与 Worker 的主机名不同
    • callbackPath — 自定义回调 URL 路径,绕开默认的 /agents/{class}/{name}/callback 构造。sendIdentityOnConnectfalse 时必填,以避免泄漏实例名。设置后,回调 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 binding
  • options(object,可选)— 连接配置:
    • props — 传给 McpAgentonStart(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,可选)— 认证成功后重定向到的 URL
    • errorRedirect(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 的持久化机制。