mcpField
C
cloudflare-mcp-server

cloudflare-mcp-server

github.com/jackspace开发1636 次安装12 周前更新
代码仓库

关于 cloudflare-mcp-server

cloudflare-mcp-server 是一项指导开发者用 TypeScript 在 Cloudflare Workers 构建和部署远程 MCP 服务的技能。内容涵盖工具定义、认证、会话状态、传输方式及部署测试,并提供模板和常见错误说明。

主要功能

  1. 01McpAgent 与工具定义模式
  2. 02SSE 和 Streamable HTTP 传输
  3. 03JWT 与 OAuth 认证方案
  4. 04使用 Durable Objects 保存会话状态
  5. 05WebSocket 休眠配置
  6. 06本地测试与 Workers 部署流程

适用场景

  1. 01从模板创建远程 MCP 服务
  2. 02为服务添加 OAuth 或令牌验证
  3. 03构建带有持久会话状态的 MCP 服务
  4. 04排查 Cloudflare MCP 部署问题

MCP 相关

该技能指导构建远程 MCP 服务,介绍如何注册工具、资源和提示词,并处理 SSE 与 Streamable HTTP 请求。

安装

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

$npx skills add https://github.com/jackspace/claudeskillz --skill cloudflare-mcp-server

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

附带文件README.mdSKILL.json

详细介绍

Cloudflare MCP 服务 Skill

使用 TypeScript 在 Cloudflare Workers 上构建和部署 模型上下文协议(MCP)服务。


这是什么 Skill?

本 Skill 将教你如何在 Cloudflare 上构建远程 MCP 服务——截至 2025 年,Cloudflare 是唯一官方支持远程 MCP 的平台。

在以下情况下使用本 Skill:

  • 使用 TypeScript(@modelcontextprotocol/sdk)构建 MCP 服务
  • 将远程 MCP 服务部署到 Cloudflare Workers
  • 实现 OAuth 身份验证(GitHub、Google、Azure、自定义)
  • 使用 Durable Objects 创建有状态的 MCP 服务
  • 使用 WebSocket 休眠优化成本
  • 同时支持 SSE 和 Streamable HTTP 传输
  • 避免 15 种以上常见的 MCP + Cloudflare 错误

你将学习:

  1. McpAgent 类模式和工具定义
  2. OAuth 集成(全部 4 种身份验证模式)
  3. 用于每会话状态的 Durable Objects
  4. WebSocket 休眠 API
  5. 双传输配置(SSE + HTTP)
  6. 完整的部署工作流

快速开始(5 分钟)

选项 1:从模板部署
# Create new MCP server from Cloudflare template
npm create cloudflare@latest -- my-mcp-server \
  --template=cloudflare/ai/demos/remote-mcp-authless

cd my-mcp-server
npm install
npm run dev

你的 MCP 服务现已运行在 http://localhost:8788/sse

选项 2:从本 Skill 复制模板
# Copy basic MCP server template
cp ~/.claude/skills/cloudflare-mcp-server/templates/basic-mcp-server.ts src/index.ts
cp ~/.claude/skills/cloudflare-mcp-server/templates/wrangler-basic.jsonc wrangler.jsonc
cp ~/.claude/skills/cloudflare-mcp-server/templates/package.json package.json

# Install dependencies
npm install

# Start development server
npm run dev
使用 MCP Inspector 测试
# In a new terminal, start MCP Inspector
npx @modelcontextprotocol/inspector@latest

# Open http://localhost:5173
# Enter your MCP server URL: http://localhost:8788/sse
# Click "Connect" and test tools
部署到 Cloudflare
# Deploy to production
npx wrangler deploy

# Your MCP server is now live at:
# https://my-mcp-server.your-account.workers.dev/sse

核心概念

1. McpAgent 类

Cloudflare Agents SDK 中的 McpAgent 基类提供:

  • 自动集成 Durable Objects
  • 内置 SQL 数据库状态管理
  • 工具、资源和提示词注册
  • 传输处理(SSE + HTTP)

基本模式:

import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

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

  async init() {
    // Register tools here
    this.server.tool(
      "tool_name",
      "Tool description",
      { param: z.string() },
      async ({ param }) => ({
        content: [{ type: "text", text: "Result" }]
      })
    );
  }
}
2. 工具定义

工具是 MCP 客户端可以调用的函数。使用 Zod 验证参数。

模式:

this.server.tool(
  "tool_name",           // Tool identifier
  "Tool description",    // What it does (for LLM)
  {                      // Parameters (Zod schema)
    param1: z.string().describe("Parameter description"),
    param2: z.number().optional()
  },
  async ({ param1, param2 }) => {  // Handler
    // Your logic here
    return {
      content: [{ type: "text", text: "Result" }]
    };
  }
);

最佳实践:

  • 详细描述:帮助大模型理解工具的用途
  • 参数描述:说明预期值和限制条件
  • 错误处理:失败时返回 { isError: true }
  • 少量、聚焦的工具:优于大量细分工具
3. 传输方式

MCP 支持两种传输方式:

SSE(服务器发送事件)——旧版,支持广泛:

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 });
  }
};

身份验证模式

Cloudflare MCP 服务支持 4 种身份验证模式:

模式 1:无需身份验证

适用场景:内部工具、开发、公共 API

模板:templates/basic-mcp-server.ts

设置:无需设置

安全性:⚠️ 任何人都可以访问你的 MCP 服务


模式 2:令牌验证(JWTVerifier)

适用场景:预先完成身份验证的客户端、自定义身份验证系统

工作原理:客户端发送 Bearer 令牌,服务端进行验证

模板:创建自定义 JWTVerifier 中间件

设置:

import { JWTVerifier } from "agents/mcp";

const verifier = new JWTVerifier({
  secret: env.JWT_SECRET,
  issuer: "your-auth-server"
});

// Validate token before serving MCP requests

安全性:✅ 正确管理令牌即可确保安全


模式 3:OAuth 代理(workers-oauth-provider)

适用场景:集成 GitHub、Google、Azure OAuth

工作原理:Cloudflare Worker 将 OAuth 请求代理到第三方提供方

模板:templates/mcp-oauth-proxy.ts

设置:

import { OAuthProvider, GitHubHandler } from "@cloudflare/workers-oauth-provider";

export default new OAuthProvider({
  authorizeEndpoint: "/authorize",
  tokenEndpoint: "/token",
  clientRegistrationEndpoint: "/register",

  defaultHandler: new GitHubHandler({
    clientId: (env) => env.GITHUB_CLIENT_ID,
    clientSecret: (env) => env.GITHUB_CLIENT_SECRET,
    scopes: ["repo", "user:email"],

    context: async (accessToken) => {
      // Fetch user info from GitHub
      const octokit = new Octokit({ auth: accessToken });
      const { data: user } = await octokit.rest.users.getAuthenticated();

      return {
        login: user.login,
        email: user.email,
        accessToken
      };
    }
  }),

  kv: (env) => env.OAUTH_KV,
  apiHandlers: {
    "/sse": MyMCP.serveSSE("/sse"),
    "/mcp": MyMCP.serve("/mcp")
  },

  allowConsentScreen: true,
  allowDynamicClientRegistration: true
});

所需绑定:

{
  "kv_namespaces": [
    { "binding": "OAUTH_KV", "id": "YOUR_KV_ID" }
  ]
}

安全性:✅✅ 安全,可用于生产环境


模式 4:使用 DCR 的远程 OAuth

适用场景:完整的 OAuth 提供方、自定义授权页面

工作原理:你的 Worker 就是 OAuth 提供方

模板:参见 Cloudflare 的 remote-mcp-authkit 演示

设置:较复杂,需要完整实现 OAuth 2.1

安全性:✅✅✅ 最安全,完全可控


使用 Durable Objects 构建有状态的 MCP 服务

当 MCP 服务需要以下功能时,请使用 Durable Objects:

  • 每会话持久状态
  • 对话历史
  • 游戏状态(国际象棋、井字棋)
  • 缓存的 API 响应
  • 用户偏好设置
存储 API 模式

模板:templates/mcp-stateful-do.ts

存储值:

await this.state.storage.put("key", "value");
await this.state.storage.put("user_prefs", { theme: "dark" });

获取值:

const value = await this.state.storage.get<string>("key");
const prefs = await this.state.storage.get<object>("user_prefs");

列出键:

const allKeys = await this.state.storage.list();

删除键:

await this.state.storage.delete("key");
配置

wrangler.jsonc:

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

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

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


用于成本优化的 WebSocket 休眠

问题:长时间运行的 WebSocket 连接会消耗 CPU 时间

解决方案:WebSocket 休眠 API 会在连接空闲时将其挂起

模式

序列化元数据(在休眠期间保留数据):

webSocket.serializeAttachment({
  userId: "123",
  sessionId: "abc",
  connectedAt: Date.now()
});

唤醒时检索:

const metadata = webSocket.deserializeAttachment();
console.log(metadata.userId); // "123"

持久化状态的存储:

// ❌ DON'T: In-memory state lost on hibernation
this.userId = "123";

// ✅ DO: Use storage API
await this.state.storage.put("userId", "123");
成本节省

未启用休眠:

  • 1000 个并发 WebSocket × 10ms CPU/秒 = 10 CPU-秒/秒
  • 成本:约 0.50 美元/天

启用休眠后:

  • 仅在收到消息时使用 CPU(99% 的空闲时间处于挂起状态)
  • 成本:约 0.01 美元/天(降低 50 倍!)

Worker 和 Durable Objects 基础知识

可独立使用的自包含章节

Worker 导出模式

Worker 必须导出 fetch 处理程序:

export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext): Response | Promise<Response> {
    // Handle request
    return new Response("Hello");
  }
};
Durable Objects 类结构

DO 继承 McpAgent(用于 MCP 服务):

export class MyMCP extends McpAgent<Env> {
  constructor(state: DurableObjectState, env: Env) {
    super(state, env);
  }

  // Your methods here
}
绑定配置

环境绑定让 Worker 能够访问资源:

{
  "kv_namespaces": [{ "binding": "MY_KV", "id": "..." }],
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
  },
  "r2_buckets": [{ "binding": "MY_BUCKET", "bucket_name": "..." }]
}

在代码中访问:

env.MY_KV.get("key");
env.MY_DO.idFromName("session-123").getStub(env);
env.MY_BUCKET.get("file.txt");

部署与测试

本地开发
# Start dev server (uses Miniflare for local DOs)
npm run dev

# Start dev server with remote Durable Objects (more accurate)
npx wrangler dev --remote

访问地址:http://localhost:8788/sse

使用 MCP Inspector 测试
npx @modelcontextprotocol/inspector@latest
  1. 打开 http://localhost:5173
  2. 输入 MCP 服务 URL
  3. 点击“Connect”
  4. 使用“List Tools”查看可用工具
  5. 使用参数测试工具调用
部署到 Cloudflare
# First time: Login
npx wrangler login

# Deploy
npx wrangler deploy

# Check deployment
npx wrangler tail

你的服务已上线,地址为:

https://my-mcp-server.YOUR_ACCOUNT.workers.dev/sse
连接 Claude Desktop

~/.config/claude/claude_desktop_config.json(Linux/Mac):

{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp-server.your-account.workers.dev/sse"
    }
  }
}

%APPDATA%/Claude/claude_desktop_config.json(Windows)

使用 OAuth:

{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp-oauth.your-account.workers.dev/sse",
      "auth": {
        "type": "oauth",
        "authorizationUrl": "https://my-mcp-oauth.your-account.workers.dev/authorize",
        "tokenUrl": "https://my-mcp-oauth.your-account.workers.dev/token"
      }
    }
  }
}

更改配置后,重启 Claude Desktop。


常见模式

API 代理 MCP 服务

使用场景:将外部 API 封装为 MCP 工具

模式:

this.server.tool(
  "search_wikipedia",
  "Search Wikipedia for a topic",
  { query: z.string() },
  async ({ query }) => {
    const response = await fetch(
      `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(query)}`
    );
    const data = await response.json();

    return {
      content: [{
        type: "text",
        text: data.extract
      }]
    };
  }
);
数据库支持的工具

使用场景:查询 D1、KV 或外部数据库

模式:

this.server.tool(
  "get_user",
  "Get user details from database",
  { userId: z.string() },
  async ({ userId }) => {
    // Query Durable Objects storage
    const user = await this.state.storage.get<User>(`user:${userId}`);

    // Or query D1 database
    const result = await env.DB.prepare(
      "SELECT * FROM users WHERE id = ?"
    ).bind(userId).first();

    return {
      content: [{
        type: "text",
        text: JSON.stringify(user || result, null, 2)
      }]
    };
  }
);
多工具协作

使用场景:调用其他工具的工具

模式:

// Store result from first tool
await this.state.storage.put("last_search", result);

// Second tool reads it
const lastSearch = await this.state.storage.get("last_search");
缓存策略

使用场景:缓存开销较大的 API 调用

模式:

this.server.tool(
  "get_weather",
  "Get weather (cached 5 minutes)",
  { city: z.string() },
  async ({ city }) => {
    const cacheKey = `weather:${city}`;
    const cached = await this.state.storage.get<CachedWeather>(cacheKey);

    // Check cache freshness
    if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
      return {
        content: [{ type: "text", text: cached.data }]
      };
    }

    // Fetch fresh data
    const weather = await fetchWeatherAPI(city);

    // Cache it
    await this.state.storage.put(cacheKey, {
      data: weather,
      timestamp: Date.now()
    });

    return {
      content: [{ type: "text", text: weather }]
    };
  }
);
使用 Durable Objects 进行速率限制

使用场景:防止滥用,遵守上游速率限制

模式:

async rateLimit(key: string, maxRequests: number, windowMs: number): Promise<boolean> {
  const now = Date.now();
  const requests = await this.state.storage.get<number[]>(`ratelimit:${key}`) || [];

  // Remove old requests outside window
  const recentRequests = requests.filter(ts => now - ts < windowMs);

  if (recentRequests.length >= maxRequests) {
    return false; // Rate limited
  }

  // Add this request
  recentRequests.push(now);
  await this.state.storage.put(`ratelimit:${key}`, recentRequests);

  return true; // Allowed
}

// Use in tool
if (!await this.rateLimit(userId, 10, 60 * 1000)) {
  return {
    content: [{ type: "text", text: "Rate limit exceeded (10 requests/minute)" }],
    isError: true
  };
}

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

1. 未导出 McpAgent 类

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

原因:忘记导出 McpAgent 类

解决方案:

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

2. 传输方式不匹配

错误:Connection failed: Unexpected response format

原因:客户端期望 /sse,但服务端只提供 /mcp

解决方案:同时提供两种传输方式(参见“传输方式”部分)


3. OAuth 重定向 URI 不匹配

错误:OAuth error: redirect_uri does not match

原因:客户端配置使用 localhost,但服务已部署到 workers.dev

解决方案:部署后更新 claude_desktop_config.json


4. WebSocket 休眠导致状态丢失

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

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

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


5. 缺少 Durable Objects 绑定

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

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

解决方案:添加绑定(参见“有状态 MCP 服务”部分)


6. 未定义迁移

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

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

解决方案:

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

7. 远程 MCP 上的 CORS 错误

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

原因:MCP 服务未返回 CORS 标头

解决方案:使用 OAuthProvider(会处理 CORS),或手动添加标头


8. 客户端配置格式错误

错误:Claude Desktop 无法识别服务

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

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


9. 未使用 serializeAttachment()

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

原因:未使用 serializeAttachment()

解决方案:参见 WebSocket 休眠部分


10. OAuth 同意屏幕已禁用

安全风险:用户看不到权限

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

解决方案:在生产环境中始终设置 allowConsentScreen: true


11. 缺少 JWT 签名密钥

错误:Error: JWT_SIGNING_KEY environment variable not set

原因:OAuth Provider 需要签名密钥

解决方案:

openssl rand -base64 32
# Add to wrangler.jsonc vars

12. 未配置环境变量

错误:env.MY_VAR is undefined

原因:变量已添加到 .dev.vars,但未添加到 wrangler.jsonc

解决方案:将其添加到 wrangler.jsonc 的 "vars" 部分


13. 工具架构验证错误

错误:ZodError: Invalid input type

原因:客户端发送字符串,但架构需要数字

解决方案:使用 Zod 转换:

z.string().transform(val => parseInt(val, 10))

14. 多个传输端点冲突

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

原因:路径匹配不正确

解决方案:使用 startsWith() 或精确匹配


15. 使用 Miniflare 进行本地测试的限制

错误:OAuth 流程在本地开发环境中失败

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

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


配置参考

完整的 wrangler.jsonc(所有功能)
{
  "name": "my-mcp-server",
  "main": "src/index.ts",
  "compatibility_date": "2025-01-01",
  "compatibility_flags": ["nodejs_compat"],
  "account_id": "YOUR_ACCOUNT_ID",

  "vars": {
    "ENVIRONMENT": "production",
    "GITHUB_CLIENT_ID": "optional-pre-configured-id"
  },

  "kv_namespaces": [
    {
      "binding": "OAUTH_KV",
      "id": "YOUR_KV_ID",
      "preview_id": "YOUR_PREVIEW_KV_ID"
    }
  ],

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

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

  "node_compat": true
}
完整的 package.json

参见 templates/package.json

完整的 claude_desktop_config.json

参见 templates/claude_desktop_config.json


其他资源

官方文档
官方示例
工具

不适用此技能的情况

以下情况请勿使用此技能:

  • 构建 Python MCP 服务(请改用 fastmcp 技能)
  • 构建仅限本地使用的 MCP 服务(请改用 typescript-mcp 技能)
  • 需要使用非 Cloudflare 托管服务(如 AWS Lambda、GCP 等)
  • 使用 Claude.ai 网页界面的技能(与 MCP 不同)

此技能专用于:TypeScript + Cloudflare Workers + 远程 MCP


版本信息

  • @modelcontextprotocol/sdk:1.21.0
  • @cloudflare/workers-oauth-provider:0.0.13
  • agents(Cloudflare Agents SDK):0.2.20
  • 上次验证:2025-11-04

已通过生产环境测试:基于 Cloudflare 官方 MCP 服务(mcp-server-cloudflare、workers-mcp)


Token 效率

不使用此技能时:

  • 查阅分散的文档:约 10k 个 token
  • 调试 15 个错误:约 30k 个 token
  • 总计:约 40k 个 token

使用此技能时:

  • 阅读技能文档:约 4k 个 token
  • 复制模板:约 1k 个 token
  • 总计:约 5k 个 token

节省:约 87%(40k → 5k 个 token)

可预防的错误:15 个(预防率 100%)


有疑问?请查阅:

  • references/authentication.md - 身份验证模式对比
  • references/transport.md - SSE 与 HTTP 的技术细节
  • references/oauth-providers.md - GitHub、Google、Azure 配置
  • references/common-issues.md - 错误排查深入指南
  • references/official-examples.md - Cloudflare 示例精选链接

评论

支持 Markdown