mcpField
C
cloudflare-mcp-server

cloudflare-mcp-server

github.com/jezweb开发1.1k328 次安装3 周前更新
代码仓库

关于 cloudflare-mcp-server

面向开发者的技能指南,帮助使用 TypeScript 在 Cloudflare Workers 上构建和部署远程 MCP 服务。内容侧重传输路径、认证、部署与常见连接问题排查。

主要功能

  1. 01Workers 上构建远程 MCP 服务
  2. 02SSE 与 Streamable HTTP 配置
  3. 03认证模板与 OAuth 设置
  4. 04路径匹配及部署排错

适用场景

  1. 01从模板创建远程 MCP 服务
  2. 02排查部署后连接返回 404
  3. 03为 MCP 服务配置 OAuth 认证

MCP 相关

该技能指导构建远程 MCP 服务,并涵盖 SSE 和 Streamable HTTP 传输、客户端 URL 配置及 OAuth。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill cloudflare-mcp-server

安装后在 Claude Code 中输入/cloudflare-mcp-server确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
commands/
init.md
references/
authentication.md
common-issues.md
debugging-guide.md
http-transport-fundamentals.md
oauth-providers.md
official-examples.md
transport-comparison.md
transport.md
rules/
cloudflare-mcp-server.md
templates/
basic-mcp-server.ts
claude_desktop_config.json
mcp-bearer-auth.ts
mcp-http-fundamentals.ts
mcp-oauth-proxy.ts
mcp-stateful-do.ts
mcp-with-d1.ts
mcp-with-workers-ai.ts
package.json
wrangler-basic.jsonc
wrangler-oauth.jsonc

详细介绍

Cloudflare MCP 服务技能

使用 TypeScript 在 Cloudflare Workers 上构建和部署 Model Context Protocol(MCP)服务。

状态:已可用于生产环境 ✅ 最后更新:2026-01-21 最新版本:@modelcontextprotocol/[email protected]、@cloudflare/[email protected]、[email protected]

近期更新(2025):

  • 2025 年 9 月:Code Mode(Agent 编写代码而非调用工具,根据架构自动生成 TypeScript API)
  • 2025 年 8 月:MCP Elicitation(交互式工作流、执行期间的用户输入)、任务队列、电子邮件集成
  • 2025 年 7 月:MCPClientManager(连接管理、OAuth 流程、休眠)
  • 2025 年 4 月:HTTP Streamable Transport(单一端点,推荐优先于 SSE)、Python MCP 支持
  • 2025 年 5 月:支持 Claude.ai 远程 MCP、use-mcp React 库、重要合作伙伴关系

这是什么技能?

此技能将教你如何在 Cloudflare 上构建远程 MCP 服务——这是唯一正式支持远程 MCP 的平台。

适用场景:避免 24 种以上常见的 MCP + Cloudflare 错误(尤其是 URL 路径不匹配——最常见的故障原因)


🚀 快速开始(5 分钟)

从 Cloudflare 官方模板开始:

npm create cloudflare@latest -- my-mcp-server \
  --template=cloudflare/ai/demos/remote-mcp-authless
cd my-mcp-server && npm install && npm run dev

根据身份验证需求选择模板:

  • remote-mcp-authless - 无需身份验证(推荐大多数场景使用)
  • remote-mcp-github-oauth - GitHub OAuth
  • remote-mcp-google-oauth - Google OAuth
  • remote-mcp-auth0 / remote-mcp-authkit - 企业级 SSO
  • mcp-server-bearer-auth - 自定义身份验证

所有模板:https://github.com/cloudflare/ai/tree/main/demos

生产环境示例:https://github.com/cloudflare/mcp-server-cloudflare(包含 15 个与实际服务集成的服务)


部署工作流

# 1. Create from template
npm create cloudflare@latest -- my-mcp --template=cloudflare/ai/demos/remote-mcp-authless
cd my-mcp && npm install && npm run dev

# 2. Deploy
npx wrangler deploy
# Note the output URL: https://my-mcp.YOUR_ACCOUNT.workers.dev

# 3. Test (PREVENTS 80% OF ERRORS!)
curl https://my-mcp.YOUR_ACCOUNT.workers.dev/sse
# Expected: {"name":"My MCP Server","version":"1.0.0","transports":["/sse","/mcp"]}
# Got 404? See "HTTP Transport Fundamentals" below

# 4. Configure client (~/.config/claude/claude_desktop_config.json)
{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp.YOUR_ACCOUNT.workers.dev/sse"  // Must match curl URL!
    }
  }
}

# 5. Restart Claude Desktop (config only loads at startup)

部署后检查清单:

  • curl 返回服务信息(而非 404)
  • 客户端 URL 与 curl URL 完全一致
  • 已重启 Claude Desktop
  • Claude Desktop 中可见工具
  • 工具调用测试成功

⚠️ 重要:HTTP 传输基础知识

MCP 服务连接失败的首要原因是 URL 路径配置错误。

URL 路径配置深入解析

在特定路径上提供 MCP 服务时,客户端 URL 必须完全匹配。

示例 1:在 /sse 上提供服务

// src/index.ts
export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const { pathname } = new URL(request.url);

    if (pathname.startsWith("/sse")) {
      return MyMCP.serveSSE("/sse").fetch(request, env, ctx);  // ← Base path is "/sse"
    }

    return new Response("Not Found", { status: 404 });
  }
};

客户端配置必须包含 /sse:

{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp.workers.dev/sse"  // ✅ Correct
    }
  }
}

❌ 错误的客户端配置:

"url": "https://my-mcp.workers.dev"      // Missing /sse → 404
"url": "https://my-mcp.workers.dev/"     // Missing /sse → 404
"url": "http://localhost:8788"           // Wrong after deploy

示例 2:在 /(根路径)上提供服务

export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return MyMCP.serveSSE("/").fetch(request, env, ctx);  // ← Base path is "/"
  }
};

客户端配置:

{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp.workers.dev"  // ✅ Correct (no /sse)
    }
  }
}

基础路径如何影响工具 URL

调用 serveSSE("/sse") 时,MCP 工具的服务地址为:

https://my-mcp.workers.dev/sse/tools/list
https://my-mcp.workers.dev/sse/tools/call
https://my-mcp.workers.dev/sse/resources/list

调用 serveSSE("/") 时,MCP 工具的服务地址为:

https://my-mcp.workers.dev/tools/list
https://my-mcp.workers.dev/tools/call
https://my-mcp.workers.dev/resources/list

基础路径会自动添加到所有 MCP 端点前。


请求/响应生命周期
1. Client connects to: https://my-mcp.workers.dev/sse
                                ↓
2. Worker receives request: { url: "https://my-mcp.workers.dev/sse", ... }
                                ↓
3. Your fetch handler: const { pathname } = new URL(request.url)
                                ↓
4. pathname === "/sse" → Check passes
                                ↓
5. MyMCP.serveSSE("/sse").fetch() → MCP server handles request
                                ↓
6. Tool calls routed to: /sse/tools/call

如果客户端连接到 https://my-mcp.workers.dev(缺少 /sse):

pathname === "/" → Check fails → 404 Not Found

测试 URL 配置

步骤 1:部署 MCP 服务

npx wrangler deploy
# Output: Deployed to https://my-mcp.YOUR_ACCOUNT.workers.dev

步骤 2:使用 curl 测试基础路径

# If serving at /sse, test this URL:
curl https://my-mcp.YOUR_ACCOUNT.workers.dev/sse

# Should return MCP server info (not 404)

步骤 3:使用你测试过的确切 URL 更新客户端配置

{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp.YOUR_ACCOUNT.workers.dev/sse"  // Match curl URL
    }
  }
}

步骤 4:重启 Claude Desktop


部署后检查清单

部署后,请确认:

  • curl https://worker.dev/sse 返回 MCP 服务信息(而非 404)
  • 客户端配置 URL 与部署的 URL 完全一致
  • URL 中没有拼写错误(常见错误:写成 workes.dev 而非 workers.dev)
  • 部署的 Workers 使用 https://(而非 http://)
  • 如果使用 OAuth,也已更新重定向 URI

传输方式选择

有两种可用的传输方式:

  1. SSE(Server-Sent Events) - 旧版,兼容性广

    MyMCP.serveSSE("/sse").fetch(request, env, ctx)
    
  2. Streamable HTTP - 2025 年标准(推荐),单一端点

    MyMCP.serve("/mcp").fetch(request, env, ctx)
    

同时支持两者以获得最大兼容性:

export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const { pathname } = new URL(request.url);

    if (pathname.startsWith("/sse")) {
      return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
    }
    if (pathname.startsWith("/mcp")) {
      return MyMCP.serve("/mcp").fetch(request, env, ctx);
    }

    return new Response("Not Found", { status: 404 });
  }
};

重要:使用 pathname.startsWith() 正确匹配路径!


2025 年的知识空白

MCP Elicitation(2025 年 8 月)

MCP 服务现在可以在工具执行期间请求用户输入:

// Request user input during tool execution
const result = await this.elicit({
  prompt: "Enter your API key:",
  type: "password"
});

// Interactive workflows with Durable Objects state
await this.state.storage.put("api_key", result);

适用场景:确认、表单、多步骤工作流 状态:在 Agent 休眠期间保留

Code Mode(2025 年 9 月)

Agents SDK 将 MCP schema 转换为 TypeScript API:

// Old: Direct tool calls
await server.callTool("get_user", { id: "123" });

// New: Type-safe generated API
const user = await api.getUser("123");

优势:自动生成文档注释、类型安全、代码补全

MCPClientManager(2025 年 7 月)

用于 MCP 客户端功能的新类:

import { MCPClientManager } from "agents/mcp";

const manager = new MCPClientManager(env);
await manager.connect("https://external-mcp.com/sse");
// Auto-discovers tools, resources, prompts
// Handles reconnection, OAuth flow, hibernation
任务队列与电子邮件(2025 年 8 月)
// Task queues for background jobs
await this.queue.send({ task: "process_data", data });

// Email integration
async onEmail(message: Email) {
  // Process incoming email
  const response = await this.generateReply(message);
  await this.sendEmail(response);
}
HTTP Streamable 传输详情(2025 年 4 月)

使用单个端点替代单独的连接和消息端点:

// Old: Separate endpoints
/connect  // Initialize connection
/message  // Send/receive messages

// New: Single streamable endpoint
/mcp      // All communication via HTTP streaming

优势:架构更简单,性能更好


安全注意事项

PKCE 绕过漏洞(严重)

CVE:GHSA-qgp8-v765-qxx9 严重性:严重 修复版本:@cloudflare/[email protected]

问题:OAuth 提供程序库的早期版本存在严重漏洞,可完全绕过 PKCE 保护,可能导致攻击者拦截授权码。

需要采取的措施:

# Check current version
npm list @cloudflare/workers-oauth-provider

# Update if < 0.0.5
npm install @cloudflare/workers-oauth-provider@latest

最低安全版本:@cloudflare/[email protected] 或更高版本

令牌存储最佳实践

OAuth 令牌务必使用加密存储:

// ✅ GOOD: workers-oauth-provider handles encryption automatically
export default new OAuthProvider({
  kv: (env) => env.OAUTH_KV,  // Tokens stored encrypted
  // ...
});

// ❌ BAD: Storing tokens in plain text
await env.KV.put("access_token", token);  // Security risk!

按用户范围限定的 KV 键可防止用户之间的数据泄漏:

// ✅ GOOD: Namespace by user ID
await env.KV.put(`user:${userId}:todos`, data);

// ❌ BAD: Global namespace
await env.KV.put(`todos`, data);  // Data visible to all users!

身份验证模式

根据使用场景选择身份验证方式:

  1. 无身份验证 - 内部工具、开发环境(模板:remote-mcp-authless)

  2. Bearer 令牌 - 自定义身份验证(模板:mcp-server-bearer-auth)

    // Validate Authorization: Bearer <token>
    const token = request.headers.get("Authorization")?.replace("Bearer ", "");
    if (!await validateToken(token, env)) {
      return new Response("Unauthorized", { status: 401 });
    }
    
  3. OAuth 代理 - GitHub/Google(模板:remote-mcp-github-oauth)

    import { OAuthProvider, GitHubHandler } from "@cloudflare/workers-oauth-provider";
    
    export default new OAuthProvider({
      authorizeEndpoint: "/authorize",
      tokenEndpoint: "/token",
      defaultHandler: new GitHubHandler({
        clientId: (env) => env.GITHUB_CLIENT_ID,
        clientSecret: (env) => env.GITHUB_CLIENT_SECRET,
        scopes: ["repo", "user:email"]
      }),
      kv: (env) => env.OAUTH_KV,
      apiHandlers: { "/sse": MyMCP.serveSSE("/sse") }
    });
    

    ⚠️ 严重:所有 OAuth URL(url、authorizationUrl、tokenUrl)必须使用相同域名

  4. 带 DCR 的远程 OAuth - 完整的 OAuth 提供程序(模板:remote-mcp-authkit)

安全级别:无身份验证(⚠️)< Bearer(✅)< OAuth 代理(✅✅)< 远程 OAuth(✅✅✅)


有状态 MCP 服务(Durable Objects)

McpAgent 扩展 Durable Objects,以支持每个会话的状态:

// Storage API
await this.state.storage.put("key", "value");
const value = await this.state.storage.get<string>("key");

// Required wrangler.jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MY_MCP", "class_name": "MyMCP" }]
  },
  "migrations": [{ "tag": "v1", "new_classes": ["MyMCP"] }]  // Required on first deploy!
}

重要:首次部署时必须进行迁移

费用:Durable Objects 现已纳入免费套餐(2025 年)


架构:内部与外部传输

重要:McpAgent 在面向客户端的通信与内部通信中使用不同的传输方式。

来源:GitHub Issue #172

传输架构
Client --- (SSE or HTTP) --> Worker --- (WebSocket) --> Durable Object

客户端 → Worker(外部):

  • SSE 传输:/sse 端点
  • HTTP Streamable:/mcp 端点
  • 客户端选择传输方式

Worker → Durable Object(内部):

  • 始终使用 WebSocket
  • PartyServer(McpAgent 的内部依赖项)要求使用
  • 自动升级,对客户端不可见
这意味着什么
  1. 完全支持 SSE 客户端 - 外部接口可以使用 SSE
  2. DO 必须使用 WebSocket - Worker 与 DO 之间的内部通信始终使用 WebSocket
  3. 这不是限制 - 这是 McpAgent 架构的实现细节
示例
export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const { pathname } = new URL(request.url);

    // Client uses SSE
    if (pathname.startsWith("/sse")) {
      // ✅ Client → Worker: SSE
      // ✅ Worker → DO: WebSocket (automatic)
      return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
    }

    return new Response("Not Found", { status: 404 });
  }
};

要点:你可以向客户端提供 SSE,无需担心内部必须使用 WebSocket。


常见模式

工具返回格式(重要)

来源:Stytch 博客 - 使用 OAuth 构建 MCP 服务

所有 MCP 工具都必须返回此确切格式:

this.server.tool(
  "my_tool",
  { /* schema */ },
  async (params) => {
    // ✅ CORRECT: Return object with content array
    return {
      content: [
        { type: "text", text: "Your result here" }
      ]
    };

    // ❌ WRONG: Raw string
    return "Your result here";

    // ❌ WRONG: Plain object
    return { result: "Your result here" };
  }
);

常见错误:返回原始字符串或普通对象,而不是规范的 MCP 内容格式。这会导致客户端解析错误。

条件式工具注册

来源:Cloudflare 博客 - 构建 AI Agent

根据已通过身份验证的用户动态添加工具:

export class MyMCP extends McpAgent<Env> {
  async init() {
    this.server = new McpServer({ name: "My MCP" });

    // Base tools for all users
    this.server.tool("public_tool", { /* schema */ }, async (params) => {
      // Available to everyone
    });

    // Conditional tools based on user
    const userId = this.props?.userId;
    if (await this.isAdmin(userId)) {
      this.server.tool("admin_tool", { /* schema */ }, async (params) => {
        // Only available to admins
      });
    }

    // Premium features
    if (await this.isPremiumUser(userId)) {
      this.server.tool("premium_feature", { /* schema */ }, async (params) => {
        // Only for premium users
      });
    }
  }

  private async isAdmin(userId?: string): Promise<boolean> {
    if (!userId) return false;
    const userRole = await this.state.storage.get<string>(`user:${userId}:role`);
    return userRole === "admin";
  }
}

使用场景:

  • 按用户启用功能标志
  • 高级版与免费版工具
  • 基于角色的访问控制(RBAC)
  • 对新工具进行 A/B 测试
使用 DO 存储进行缓存
async getCached<T>(key: string, ttlMs: number, fetchFn: () => Promise<T>): Promise<T> {
  const cached = await this.state.storage.get<{ data: T, timestamp: number }>(key);
  if (cached && Date.now() - cached.timestamp < ttlMs) {
    return cached.data;
  }
  const data = await fetchFn();
  await this.state.storage.put(key, { data, timestamp: Date.now() });
  return data;
}
速率限制
async rateLimit(key: string, maxRequests: number, windowMs: number): Promise<boolean> {
  const requests = await this.state.storage.get<number[]>(`ratelimit:${key}`) || [];
  const recentRequests = requests.filter(ts => Date.now() - ts < windowMs);
  if (recentRequests.length >= maxRequests) return false;
  recentRequests.push(Date.now());
  await this.state.storage.put(`ratelimit:${key}`, recentRequests);
  return true;
}

24 个已知错误(及解决方案)

1. 未导出 McpAgent 类

错误:TypeError: Cannot read properties of undefined (reading 'serve')

原因:忘记导出 McpAgent 类

解决方案:

export class MyMCP extends McpAgent { ... }  // ✅ Must export
export default { fetch() { ... } }

2. 基础路径配置不匹配(最常见!)

错误:404 Not Found 或 Connection failed

原因:serveSSE("/sse"),但客户端配置为 https://worker.dev(缺少 /sse)

解决方案:确保基础路径完全匹配

// Server serves at /sse
MyMCP.serveSSE("/sse").fetch(...)

// Client MUST include /sse
{ "url": "https://worker.dev/sse" }  // ✅ Correct
{ "url": "https://worker.dev" }      // ❌ Wrong - 404

调试步骤:

  1. 检查服务使用的路径:serveSSE("/sse") 还是 serveSSE("/")
  2. 使用 curl 测试:curl https://worker.dev/sse
  3. 更新客户端配置,使其与 curl URL 匹配

3. 传输类型混淆

错误:Connection failed: Unexpected response format

原因:客户端期望 SSE,却连接到 HTTP 端点(或反之)

解决方案:确保传输类型匹配

// SSE transport
MyMCP.serveSSE("/sse")  // Client URL: https://worker.dev/sse

// HTTP transport
MyMCP.serve("/mcp")     // Client URL: https://worker.dev/mcp

最佳实践:同时支持两种传输方式(参见传输方式选择指南)


4. pathname.startsWith() 逻辑错误

错误:/sse 和 /mcp 路由均失败或发生冲突

原因:路径匹配逻辑不正确

解决方案:正确使用 startsWith()

// ✅ CORRECT
if (pathname.startsWith("/sse")) {
  return MyMCP.serveSSE("/sse").fetch(...);
}
if (pathname.startsWith("/mcp")) {
  return MyMCP.serve("/mcp").fetch(...);
}

// ❌ WRONG: Exact match breaks sub-paths
if (pathname === "/sse") {  // Breaks /sse/tools/list
  return MyMCP.serveSSE("/sse").fetch(...);
}

5. 本地 URL 与部署 URL 不匹配

错误:开发环境中正常,部署后失败

原因:客户端仍配置为 localhost URL

解决方案:部署后更新客户端配置

// Development
{ "url": "http://localhost:8788/sse" }

// ⚠️ MUST UPDATE after npx wrangler deploy
{ "url": "https://my-mcp.YOUR_ACCOUNT.workers.dev/sse" }

部署后检查清单:

  • 运行 npx wrangler deploy 并记下输出的 URL
  • 使用部署后的 URL 更新客户端配置
  • 使用 curl 测试
  • 重启 Claude Desktop

6. OAuth 重定向 URI 不匹配

错误:OAuth error: redirect_uri does not match

原因:OAuth 重定向 URI 与部署后的 URL 不匹配

解决方案:部署后更新所有 OAuth URL

{
  "url": "https://my-mcp.YOUR_ACCOUNT.workers.dev/sse",
  "auth": {
    "type": "oauth",
    "authorizationUrl": "https://my-mcp.YOUR_ACCOUNT.workers.dev/authorize",  // Must match deployed domain
    "tokenUrl": "https://my-mcp.YOUR_ACCOUNT.workers.dev/token"
  }
}

重要:所有 URL 必须使用相同的协议和域名!


7. 缺少 OPTIONS 处理程序(CORS 预检)

错误:Access to fetch at '...' blocked by CORS policy 或 Method Not Allowed

原因:基于浏览器的客户端会发送 OPTIONS 请求进行 CORS 预检,但服务未处理这些请求

解决方案:添加 OPTIONS 处理程序

export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext) {
    // Handle CORS preflight
    if (request.method === "OPTIONS") {
      return new Response(null, {
        status: 204,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
          "Access-Control-Allow-Headers": "Content-Type, Authorization",
          "Access-Control-Max-Age": "86400"
        }
      });
    }

    // ... rest of your fetch handler
  }
};

适用场景:基于浏览器的 MCP 客户端(例如浏览器中的 MCP Inspector)


8. 缺少请求正文验证

错误:TypeError: Cannot read properties of undefined 或解析 JSON 时出现 Unexpected token

原因:客户端发送格式错误的 JSON,服务在解析前未进行验证

解决方案:将请求处理封装在 try/catch 中

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    try {
      // Your MCP server logic
      return await MyMCP.serveSSE("/sse").fetch(request, env, ctx);
    } catch (error) {
      console.error("Request handling error:", error);
      return new Response(
        JSON.stringify({
          error: "Invalid request",
          details: error.message
        }),
        {
          status: 400,
          headers: { "Content-Type": "application/json" }
        }
      );
    }
  }
};

9. 缺少环境变量验证

错误:TypeError: env.API_KEY is undefined 或静默失败(工具返回空数据)

原因:未配置必需的环境变量,或运行时缺少这些变量

解决方案:添加启动时验证

export class MyMCP extends McpAgent<Env> {
  async init() {
    // Validate required environment variables
    if (!this.env.API_KEY) {
      throw new Error("API_KEY environment variable not configured");
    }
    if (!this.env.DATABASE_URL) {
      throw new Error("DATABASE_URL environment variable not configured");
    }

    // Continue with tool registration
    this.server.tool(...);
  }
}

配置检查清单:

  • 开发环境:添加到 .dev.vars(仅限本地,已加入 gitignore)
  • 生产环境:将变量添加到 wrangler.jsonc 的 vars(公开变量),或使用 wrangler secret(敏感变量)

最佳实践:

# .dev.vars (local development, gitignored)
API_KEY=dev-key-123
DATABASE_URL=http://localhost:3000

# wrangler.jsonc (public config)
{
  "vars": {
    "ENVIRONMENT": "production",
    "LOG_LEVEL": "info"
  }
}

# wrangler secret (production secrets)
npx wrangler secret put API_KEY
npx wrangler secret put DATABASE_URL

10. McpAgent 与 McpServer 混淆

错误:TypeError: server.registerTool is not a function 或 this.server is undefined

原因:尝试在 McpAgent 类中使用独立 SDK 模式

解决方案:使用 McpAgent 的 this.server.tool() 模式

// ❌ WRONG: Mixing standalone SDK with McpAgent
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({ name: "My Server" });
server.registerTool(...);  // Not compatible with McpAgent!

export class MyMCP extends McpAgent { /* no server property */ }

// ✅ CORRECT: McpAgent pattern
export class MyMCP extends McpAgent<Env> {
  server = new McpServer({
    name: "My MCP Server",
    version: "1.0.0"
  });

  async init() {
    this.server.tool("tool_name", ...);  // Use this.server
  }
}

主要区别:McpAgent 提供 this.server 属性,独立 SDK 不提供。


11. WebSocket 休眠导致状态丢失

错误:重新连接后,工具调用失败并提示“state not found”

原因:休眠时内存中的状态会被清除

解决方案:使用 this.state.storage,而不是实例属性

// ❌ DON'T: Lost on hibernation
this.userId = "123";

// ✅ DO: Persists through hibernation
await this.state.storage.put("userId", "123");

12. 缺少 Durable Objects 绑定

错误:TypeError: Cannot read properties of undefined (reading 'idFromName')

原因:忘记在 wrangler.jsonc 中配置 DO 绑定

解决方案:添加绑定(参见有状态 MCP 服务一节)

{
  "durable_objects": {
    "bindings": [
      {
        "name": "MY_MCP",
        "class_name": "MyMCP",
        "script_name": "my-mcp-server"
      }
    ]
  }
}

13. 未定义迁移

错误:Error: Durable Object class MyMCP has no migration defined

原因:首次部署 DO 需要迁移

解决方案:

{
  "migrations": [
    { "tag": "v1", "new_classes": ["MyMCP"] }
  ]
}

14. 未使用 serializeAttachment()

错误:休眠唤醒时 WebSocket 元数据丢失

原因:未使用 serializeAttachment() 保留连接元数据

解决方案:请参阅 WebSocket 休眠部分


15. OAuth 同意屏幕已禁用

安全风险:用户看不到自己授予了哪些权限

原因:生产环境中设置了 allowConsentScreen: false

解决方案:生产环境中始终启用

export default new OAuthProvider({
  allowConsentScreen: true,  // ✅ Always true in production
  // ...
});

16. 缺少 JWT 签名密钥

错误:Error: JWT_SIGNING_KEY environment variable not set

原因:OAuth Provider 需要签名密钥来签发令牌

解决方案:

# Generate secure key
openssl rand -base64 32

# Add to wrangler secret
npx wrangler secret put JWT_SIGNING_KEY

17. 工具架构验证错误

错误:ZodError: Invalid input type

原因:客户端发送字符串,而架构要求数字(或反之)

解决方案:使用 Zod 转换

// Accept string, convert to number
param: z.string().transform(val => parseInt(val, 10))

// Or: Accept both types
param: z.union([z.string(), z.number()]).transform(val =>
  typeof val === "string" ? parseInt(val, 10) : val
)

18. 多个传输端点冲突

错误:添加 /mcp 后,/sse 返回 404

原因:路径匹配不正确(缺少 startsWith())

解决方案:正确使用 startsWith() 或精确匹配(参见错误 #4)


19. Miniflare 的本地测试限制

错误:本地开发时 OAuth 流程失败,或 Durable Objects 的行为不同

原因:Miniflare 不支持所有 DO 功能

解决方案:使用 npx wrangler dev --remote 获得完整的 DO 支持

# Local simulation (faster but limited)
npm run dev

# Remote DOs (slower but accurate)
npx wrangler dev --remote

20. 客户端配置格式错误

错误:Claude Desktop 无法识别服务

原因:claude_desktop_config.json 中的 JSON 格式错误

解决方案:请参阅“连接 Claude Desktop”部分,了解正确格式

常见错误:

// ❌ WRONG: Missing "mcpServers" wrapper
{
  "my-mcp": {
    "url": "https://worker.dev/sse"
  }
}

// ❌ WRONG: Trailing comma
{
  "mcpServers": {
    "my-mcp": {
      "url": "https://worker.dev/sse",  // ← Remove comma
    }
  }
}

// ✅ CORRECT
{
  "mcpServers": {
    "my-mcp": {
      "url": "https://worker.dev/sse"
    }
  }
}

21. 缺少运行状况检查端点

问题:无法判断 Worker 是否正在运行或 URL 是否正确

影响:排查连接问题需要更长时间

解决方案:添加运行状况检查端点(参见传输方式选择指南)

测试:

curl https://my-mcp.workers.dev/health
# Should return: {"status":"ok","transports":{...}}

22. 缺少 CORS 标头

错误:Access to fetch at '...' blocked by CORS policy

原因:MCP 服务未针对跨源请求返回 CORS 标头

解决方案:为所有响应添加 CORS 标头

// Manual CORS (if not using OAuthProvider)
const corsHeaders = {
  "Access-Control-Allow-Origin": "*",  // Or specific origin
  "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization"
};

// Add to responses
return new Response(body, {
  headers: {
    ...corsHeaders,
    "Content-Type": "application/json"
  }
});

注意:OAuthProvider 会自动处理 CORS!


23. MCP 初始化期间 IoContext 超时

错误:IoContext timed out due to inactivity, waitUntil tasks were cancelled

来源:GitHub Issue #640

原因:使用带有自定义 Bearer 身份验证的 McpAgent 实现 MCP 服务时,IoContext 会在 MCP 协议初始化握手期间超时(此时尚未调用任何工具)。

症状:

  • 在调用任何工具之前就发生超时
  • 初始请求与 Agent 初始化之间间隔约 2 分钟
  • 内部方法可以正常工作(setInitializeRequest、getInitializeRequest、updateProps)
  • 对 /mcp 的 GET 和 POST 请求都会被取消
  • 错误:IoContext timed out due to inactivity, waitUntil tasks were cancelled

受影响的代码模式:

// Custom Bearer auth without OAuthProvider wrapper
export default {
  fetch: async (req, env, ctx) => {
    const authHeader = req.headers.get("Authorization");
    if (!authHeader?.startsWith("Bearer ")) {
      return new Response("Unauthorized", { status: 401 });
    }

    if (url.pathname === "/sse") {
      return MyMCP.serveSSE("/sse")(req, env, ctx);  // ← Timeout here
    }
    return new Response("Not found", { status: 404 });
  }
};

根本原因假设:

  • 即使使用自定义 Bearer 身份验证,可能也需要 OAuthProvider 包装器
  • Durable Object IoContext 可能缺少超时配置
  • 可能需要使用 CloudflareMCPServer,而非标准的 McpServer

临时解决方案:使用采用 OAuthProvider 模式的官方模板,而非自定义 Bearer 身份验证:

// Use OAuthProvider wrapper (recommended)
import { OAuthProvider } from "@cloudflare/workers-oauth-provider";

export default new OAuthProvider({
  authorizeEndpoint: "/authorize",
  tokenEndpoint: "/token",
  // ... OAuth config
  apiHandlers: { "/sse": MyMCP.serveSSE("/sse") }
});

状态:仍在调查中(截至 2026-01-21,问题仍未关闭)


24. OAuth 远程连接失败

错误:使用 OAuth 时无法连接到远程 MCP 服务(本地运行正常,部署后失败)

来源:GitHub Issue #444

原因:将 Cloudflare Agents 仓库中的 MCP 客户端部署到 Workers 后,客户端无法连接到受 OAuth 保护的 MCP 服务。

症状:

  • 本地开发时运行正常
  • 部署到 Workers 后失败
  • OAuth 握手始终无法完成
  • 客户端无法建立连接

故障排查步骤:

  1. 验证远程连接期间是否正确处理 OAuth 令牌

    // Check token is being passed to remote server
    console.log("Connecting with token:", token ? "present" : "missing");
    
  2. 检查访问 OAuth 提供方的网络权限

    // Ensure Worker can reach OAuth endpoints
    const response = await fetch("https://oauth-provider.com/token");
    
  3. 检查 OAuth 提供方的 CORS 配置

    // OAuth provider must allow Worker origin
    headers: {
      "Access-Control-Allow-Origin": "https://your-worker.workers.dev",
      "Access-Control-Allow-Methods": "POST, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type, Authorization"
    }
    
  4. 检查重定向 URI 是否与部署后的 URL 相符

    {
      "url": "https://mcp.workers.dev/sse",
      "auth": {
        "authorizationUrl": "https://mcp.workers.dev/authorize",  // Must match deployed domain
        "tokenUrl": "https://mcp.workers.dev/token"
      }
    }
    

部署检查清单:

  • 所有 OAuth URL 均使用部署后的域名(而非 localhost)
  • 已在 OAuth 提供方配置 CORS 标头
  • Worker 允许向 OAuth 提供方发送网络请求
  • 已在 OAuth 提供方注册重定向 URI
  • 已在生产环境中设置环境变量(wrangler secret)

相关:Issue #640(两者都涉及远程部署中的 OAuth/身份验证)


测试与部署

# Local dev
npm run dev                    # Miniflare (fast)
npx wrangler dev --remote      # Remote DOs (accurate)

# Test with MCP Inspector
npx @modelcontextprotocol/inspector@latest
# Open http://localhost:5173, enter http://localhost:8788/sse

# Deploy
npx wrangler login  # First time only
npx wrangler deploy
# ⚠️ CRITICAL: Update client config with deployed URL!

# Monitor logs
npx wrangler tail

官方文档


软件包版本:@modelcontextprotocol/[email protected]、@cloudflare/[email protected]、[email protected] 最后验证日期:2026-01-21 已预防错误:24 个已记录的问题(预防率 100%) 技能版本:3.1.0 | 变更:新增 IoContext 超时(#23)、OAuth 远程连接失败(#24)、安全章节(PKCE 漏洞)、架构说明(内部 WebSocket)、工具返回格式模式、有条件的工具注册

评论

支持 Markdown