关于 cloudflare-mcp-server
面向开发者的技能指南,帮助使用 TypeScript 在 Cloudflare Workers 上构建和部署远程 MCP 服务。内容侧重传输路径、认证、部署与常见连接问题排查。
主要功能
- 01Workers 上构建远程 MCP 服务
- 02SSE 与 Streamable HTTP 配置
- 03认证模板与 OAuth 设置
- 04路径匹配及部署排错
适用场景
- 01从模板创建远程 MCP 服务
- 02排查部署后连接返回 404
- 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.jsoncommands/init.mdreferences/authentication.mdcommon-issues.mddebugging-guide.mdhttp-transport-fundamentals.mdoauth-providers.mdofficial-examples.mdtransport-comparison.mdtransport.mdrules/cloudflare-mcp-server.mdtemplates/basic-mcp-server.tsclaude_desktop_config.jsonmcp-bearer-auth.tsmcp-http-fundamentals.tsmcp-oauth-proxy.tsmcp-stateful-do.tsmcp-with-d1.tsmcp-with-workers-ai.tspackage.jsonwrangler-basic.jsoncwrangler-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 OAuthremote-mcp-google-oauth- Google OAuthremote-mcp-auth0/remote-mcp-authkit- 企业级 SSOmcp-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
传输方式选择
有两种可用的传输方式:
-
SSE(Server-Sent Events) - 旧版,兼容性广
MyMCP.serveSSE("/sse").fetch(request, env, ctx) -
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!
身份验证模式
根据使用场景选择身份验证方式:
-
无身份验证 - 内部工具、开发环境(模板:
remote-mcp-authless) -
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 }); } -
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)必须使用相同域名
-
带 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 在面向客户端的通信与内部通信中使用不同的传输方式。
传输架构
Client --- (SSE or HTTP) --> Worker --- (WebSocket) --> Durable Object
客户端 → Worker(外部):
- SSE 传输:
/sse端点 - HTTP Streamable:
/mcp端点 - 客户端选择传输方式
Worker → Durable Object(内部):
- 始终使用 WebSocket
- PartyServer(McpAgent 的内部依赖项)要求使用
- 自动升级,对客户端不可见
这意味着什么
- 完全支持 SSE 客户端 - 外部接口可以使用 SSE
- DO 必须使用 WebSocket - Worker 与 DO 之间的内部通信始终使用 WebSocket
- 这不是限制 - 这是 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
调试步骤:
- 检查服务使用的路径:
serveSSE("/sse")还是serveSSE("/") - 使用 curl 测试:
curl https://worker.dev/sse - 更新客户端配置,使其与 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
原因:使用带有自定义 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 服务(本地运行正常,部署后失败)
原因:将 Cloudflare Agents 仓库中的 MCP 客户端部署到 Workers 后,客户端无法连接到受 OAuth 保护的 MCP 服务。
症状:
- 本地开发时运行正常
- 部署到 Workers 后失败
- OAuth 握手始终无法完成
- 客户端无法建立连接
故障排查步骤:
-
验证远程连接期间是否正确处理 OAuth 令牌
// Check token is being passed to remote server console.log("Connecting with token:", token ? "present" : "missing"); -
检查访问 OAuth 提供方的网络权限
// Ensure Worker can reach OAuth endpoints const response = await fetch("https://oauth-provider.com/token"); -
检查 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" } -
检查重定向 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
官方文档
- Cloudflare Agents:https://developers.cloudflare.com/agents/
- MCP 规范:https://modelcontextprotocol.io/
- 官方模板:https://github.com/cloudflare/ai/tree/main/demos
- 生产环境服务:https://github.com/cloudflare/mcp-server-cloudflare
- workers-oauth-provider:https://github.com/cloudflare/workers-oauth-provider
软件包版本:@modelcontextprotocol/[email protected]、@cloudflare/[email protected]、[email protected] 最后验证日期:2026-01-21 已预防错误:24 个已记录的问题(预防率 100%) 技能版本:3.1.0 | 变更:新增 IoContext 超时(#23)、OAuth 远程连接失败(#24)、安全章节(PKCE 漏洞)、架构说明(内部 WebSocket)、工具返回格式模式、有条件的工具注册