cloudflare-mcp-server

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare MCP Server Skill

Cloudflare MCP 服务器技能指南

Build and deploy Model Context Protocol (MCP) servers on Cloudflare Workers with TypeScript.

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

What is This Skill?

本技能简介

This skill teaches you to build remote MCP servers on Cloudflare - the ONLY platform with official remote MCP support as of 2025.
Use this skill when:
  • Building MCP servers with TypeScript (@modelcontextprotocol/sdk)
  • Deploying remote MCP servers to Cloudflare Workers
  • Implementing OAuth authentication (GitHub, Google, Azure, custom)
  • Creating stateful MCP servers with Durable Objects
  • Optimizing costs with WebSocket hibernation
  • Supporting both SSE and Streamable HTTP transports
  • Avoiding 15+ common MCP + Cloudflare errors
You'll learn:
  1. McpAgent class patterns and tool definitions
  2. OAuth integration (all 4 auth patterns)
  3. Durable Objects for per-session state
  4. WebSocket hibernation API
  5. Dual transport configuration (SSE + HTTP)
  6. Complete deployment workflow

本技能将教你在Cloudflare上构建远程MCP服务器——截至2025年,Cloudflare是唯一官方支持远程MCP的平台。
以下场景请使用本技能
  • 使用TypeScript(@modelcontextprotocol/sdk)构建MCP服务器
  • 将远程MCP服务器部署到Cloudflare Workers
  • 实现OAuth认证(GitHub、Google、Azure、自定义)
  • 利用Durable Objects创建有状态MCP服务器
  • 通过WebSocket休眠优化成本
  • 同时支持SSE和可流式HTTP传输
  • 避免15+种MCP + Cloudflare常见错误
你将学到
  1. McpAgent类模式与工具定义
  2. OAuth集成(全部4种认证模式)
  3. 利用Durable Objects实现会话级状态管理
  4. WebSocket休眠API
  5. 双传输配置(SSE + HTTP)
  6. 完整部署流程

Quick Start (5 Minutes)

快速开始(5分钟)

Option 1: Deploy from Template

选项1:从模板部署

bash
undefined
bash
undefined

Create new MCP server from Cloudflare template

从Cloudflare模板创建新的MCP服务器

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

Your MCP server is now running at `http://localhost:8788/sse`
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` 运行

Option 2: Copy Templates from This Skill

选项2:从本技能复制模板

bash
undefined
bash
undefined

Copy basic MCP server template

复制基础MCP服务器模板

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
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
npm install

Start development server

启动开发服务器

npm run dev
undefined
npm run dev
undefined

Test with MCP Inspector

使用MCP Inspector测试

bash
undefined
bash
undefined

In a new terminal, start MCP Inspector

在新终端中启动MCP Inspector

npx @modelcontextprotocol/inspector@latest
npx @modelcontextprotocol/inspector@latest

Enter your MCP server URL: http://localhost:8788/sse

输入你的MCP服务器URL:http://localhost:8788/sse

Click "Connect" and test tools

点击“连接”并测试工具

undefined
undefined

Deploy to Cloudflare

部署到Cloudflare

bash
undefined
bash
undefined

Deploy to production

部署到生产环境

npx wrangler deploy
npx wrangler deploy

Your MCP server is now live at:

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


---

---

Core Concepts

核心概念

1. McpAgent Class

1. McpAgent类

The
McpAgent
base class from Cloudflare's Agents SDK provides:
  • Automatic Durable Objects integration
  • Built-in state management with SQL database
  • Tool, resource, and prompt registration
  • Transport handling (SSE + HTTP)
Basic pattern:
typescript
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" }]
      })
    );
  }
}
Cloudflare Agents SDK提供的
McpAgent
基类具备以下能力:
  • 自动集成Durable Objects
  • 内置SQL数据库状态管理
  • 工具、资源和提示词注册
  • 传输协议处理(SSE + HTTP)
基础模式:
typescript
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() {
    // 在此注册工具
    this.server.tool(
      "tool_name",
      "Tool description",
      { param: z.string() },
      async ({ param }) => ({
        content: [{ type: "text", text: "Result" }]
      })
    );
  }
}

2. Tool Definitions

2. 工具定义

Tools are functions that MCP clients can invoke. Use Zod for parameter validation.
Pattern:
typescript
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" }]
    };
  }
);
Best practices:
  • Detailed descriptions: Help LLMs understand tool purpose
  • Parameter descriptions: Explain expected values and constraints
  • Error handling: Return
    { isError: true }
    for failures
  • Few, focused tools: Better than many granular ones
工具是MCP客户端可以调用的函数。使用Zod进行参数验证。
模式:
typescript
this.server.tool(
  "tool_name",           // 工具标识符
  "Tool description",    // 工具功能说明(供大语言模型使用)
  {                      // 参数(Zod schema)
    param1: z.string().describe("Parameter description"),
    param2: z.number().optional()
  },
  async ({ param1, param2 }) => {  // 处理函数
    // 你的业务逻辑
    return {
      content: [{ type: "text", text: "Result" }]
    };
  }
);
最佳实践:
  • 详细描述:帮助大语言模型理解工具用途
  • 参数描述:说明预期值和约束条件
  • 错误处理:失败时返回
    { isError: true }
  • 少而精的工具:优于多个粒度极细的工具

3. Transport Methods

3. 传输方式

MCP supports two transports:
SSE (Server-Sent Events) - Legacy, widely supported:
typescript
MyMCP.serveSSE("/sse").fetch(request, env, ctx)
Streamable HTTP - 2025 standard, more efficient:
typescript
MyMCP.serve("/mcp").fetch(request, env, ctx)
Support both for maximum compatibility:
typescript
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 });
  }
};

MCP支持两种传输协议:
SSE(Server-Sent Events) - 传统方案,兼容性广:
typescript
MyMCP.serveSSE("/sse").fetch(request, env, ctx)
可流式HTTP - 2025年标准,效率更高:
typescript
MyMCP.serve("/mcp").fetch(request, env, ctx)
同时支持两种协议以实现最大兼容性:
typescript
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 });
  }
};

Authentication Patterns

认证模式

Cloudflare MCP servers support 4 authentication patterns:
Cloudflare MCP服务器支持4种认证模式:

Pattern 1: No Authentication

模式1:无认证

Use case: Internal tools, development, public APIs
Template:
templates/basic-mcp-server.ts
Setup: None required
Security: ⚠️ Anyone can access your MCP server

适用场景:内部工具、开发环境、公开API
模板
templates/basic-mcp-server.ts
配置:无需额外设置
安全性:⚠️ 任何人都可以访问你的MCP服务器

Pattern 2: Token Validation (JWTVerifier)

模式2:令牌验证(JWTVerifier)

Use case: Pre-authenticated clients, custom auth systems
How it works: Client sends Bearer token, server validates
Template: Create custom JWTVerifier middleware
Setup:
typescript
import { JWTVerifier } from "agents/mcp";

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

// Validate token before serving MCP requests
Security: ✅ Secure if tokens are properly managed

适用场景:预认证客户端、自定义认证系统
工作原理:客户端发送Bearer令牌,服务器验证有效性
模板:创建自定义JWTVerifier中间件
配置:
typescript
import { JWTVerifier } from "agents/mcp";

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

// 在处理MCP请求前验证令牌
安全性:✅ 若令牌管理得当则安全

Pattern 3: OAuth Proxy (workers-oauth-provider)

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

Use case: GitHub, Google, Azure OAuth integration
How it works: Cloudflare Worker proxies OAuth to third-party provider
Template:
templates/mcp-oauth-proxy.ts
Setup:
typescript
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
});
Required bindings:
jsonc
{
  "kv_namespaces": [
    { "binding": "OAUTH_KV", "id": "YOUR_KV_ID" }
  ]
}
Security: ✅✅ Secure, production-ready

适用场景:GitHub、Google、Azure OAuth集成
工作原理:Cloudflare Worker将OAuth请求代理到第三方提供商
模板
templates/mcp-oauth-proxy.ts
配置:
typescript
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) => {
      // 从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
});
必需绑定:
jsonc
{
  "kv_namespaces": [
    { "binding": "OAUTH_KV", "id": "YOUR_KV_ID" }
  ]
}
安全性:✅✅ 安全,适合生产环境

Pattern 4: Remote OAuth with DCR

模式4:带DCR的远程OAuth

Use case: Full OAuth provider, custom consent screens
How it works: Your Worker is the OAuth provider
Template: See Cloudflare's
remote-mcp-authkit
demo
Setup: Complex, requires full OAuth 2.1 implementation
Security: ✅✅✅ Most secure, full control

适用场景:完整OAuth提供商、自定义授权界面
工作原理:你的Worker作为OAuth提供商
模板:参考Cloudflare的
remote-mcp-authkit
示例
配置:复杂,需要完整实现OAuth 2.1
安全性:✅✅✅ 最安全,完全可控

Stateful MCP Servers with Durable Objects

基于Durable Objects的有状态MCP服务器

Use Durable Objects when your MCP server needs:
  • Per-session persistent state
  • Conversation history
  • Game state (chess, tic-tac-toe)
  • Cached API responses
  • User preferences
当你的MCP服务器需要以下能力时,请使用Durable Objects:
  • 会话级持久化状态
  • 对话历史
  • 游戏状态(国际象棋、井字棋)
  • 缓存API响应
  • 用户偏好设置

Storage API Pattern

存储API模式

Template:
templates/mcp-stateful-do.ts
Store values:
typescript
await this.state.storage.put("key", "value");
await this.state.storage.put("user_prefs", { theme: "dark" });
Retrieve values:
typescript
const value = await this.state.storage.get<string>("key");
const prefs = await this.state.storage.get<object>("user_prefs");
List keys:
typescript
const allKeys = await this.state.storage.list();
Delete keys:
typescript
await this.state.storage.delete("key");
模板
templates/mcp-stateful-do.ts
存储值:
typescript
await this.state.storage.put("key", "value");
await this.state.storage.put("user_prefs", { theme: "dark" });
获取值:
typescript
const value = await this.state.storage.get<string>("key");
const prefs = await this.state.storage.get<object>("user_prefs");
列出键:
typescript
const allKeys = await this.state.storage.list();
删除键:
typescript
await this.state.storage.delete("key");

Configuration

配置

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

  "migrations": [
    { "tag": "v1", "new_classes": ["MyMCP"] }
  ]
}
IMPORTANT: Migrations are required on first deployment!

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

  "migrations": [
    { "tag": "v1", "new_classes": ["MyMCP"] }
  ]
}
重要提示:首次部署时必须配置迁移!

WebSocket Hibernation for Cost Optimization

WebSocket休眠以优化成本

Problem: Long-lived WebSocket connections cost CPU time
Solution: WebSocket Hibernation API suspends connections when idle
问题:长连接WebSocket会持续消耗CPU时间
解决方案:WebSocket休眠API会在连接空闲时暂停连接

Pattern

模式

Serialize metadata (preserves data during hibernation):
typescript
webSocket.serializeAttachment({
  userId: "123",
  sessionId: "abc",
  connectedAt: Date.now()
});
Retrieve on wake:
typescript
const metadata = webSocket.deserializeAttachment();
console.log(metadata.userId); // "123"
Storage for persistent state:
typescript
// ❌ DON'T: In-memory state lost on hibernation
this.userId = "123";

// ✅ DO: Use storage API
await this.state.storage.put("userId", "123");
序列化元数据(休眠期间保留数据):
typescript
webSocket.serializeAttachment({
  userId: "123",
  sessionId: "abc",
  connectedAt: Date.now()
});
唤醒时检索:
typescript
const metadata = webSocket.deserializeAttachment();
console.log(metadata.userId); // "123"
持久化状态存储:
typescript
// ❌ 错误做法:内存状态会在休眠时丢失
this.userId = "123";

// ✅ 正确做法:使用存储API
await this.state.storage.put("userId", "123");

Cost Savings

成本节省

Without hibernation:
  • 1000 concurrent WebSockets × 10ms CPU/sec = 10 CPU-sec/sec
  • Cost: ~$0.50/day
With hibernation:
  • CPU only on messages (99% idle time suspended)
  • Cost: ~$0.01/day (50x reduction!)

无休眠时:
  • 1000个并发WebSocket × 10ms CPU/秒 = 10 CPU秒/秒
  • 成本:约0.50美元/天
启用休眠后:
  • 仅在有消息时消耗CPU(99%空闲时间被暂停)
  • 成本:约0.01美元/天(降低50倍!)

Worker & Durable Objects Basics

Worker与Durable Objects基础知识

Self-contained section for standalone use
独立章节,可单独使用

Worker Export Pattern

Worker导出模式

Workers must export a
fetch
handler
:
typescript
export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext): Response | Promise<Response> {
    // Handle request
    return new Response("Hello");
  }
};
Worker必须导出
fetch
处理函数
:
typescript
export default {
  fetch(request: Request, env: Env, ctx: ExecutionContext): Response | Promise<Response> {
    // 处理请求
    return new Response("Hello");
  }
};

Durable Objects Class Structure

Durable Objects类结构

DOs extend McpAgent (for MCP servers):
typescript
export class MyMCP extends McpAgent<Env> {
  constructor(state: DurableObjectState, env: Env) {
    super(state, env);
  }

  // Your methods here
}
DO需继承McpAgent(适用于MCP服务器):
typescript
export class MyMCP extends McpAgent<Env> {
  constructor(state: DurableObjectState, env: Env) {
    super(state, env);
  }

  // 你的方法
}

Bindings Configuration

绑定配置

Environment bindings give Workers access to resources:
jsonc
{
  "kv_namespaces": [{ "binding": "MY_KV", "id": "..." }],
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
  },
  "r2_buckets": [{ "binding": "MY_BUCKET", "bucket_name": "..." }]
}
Access in code:
typescript
env.MY_KV.get("key");
env.MY_DO.idFromName("session-123").getStub(env);
env.MY_BUCKET.get("file.txt");

环境绑定让Worker可以访问资源:
jsonc
{
  "kv_namespaces": [{ "binding": "MY_KV", "id": "..." }],
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
  },
  "r2_buckets": [{ "binding": "MY_BUCKET", "bucket_name": "..." }]
}
代码中访问:
typescript
env.MY_KV.get("key");
env.MY_DO.idFromName("session-123").getStub(env);
env.MY_BUCKET.get("file.txt");

Deployment & Testing

部署与测试

Local Development

本地开发

bash
undefined
bash
undefined

Start dev server (uses Miniflare for local DOs)

启动开发服务器(使用Miniflare模拟本地DO)

npm run dev
npm run dev

Start dev server with remote Durable Objects (more accurate)

启动开发服务器并使用远程Durable Objects(更接近生产环境)

npx wrangler dev --remote

**Access at**: `http://localhost:8788/sse`
npx wrangler dev --remote

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

Test with MCP Inspector

使用MCP Inspector测试

bash
npx @modelcontextprotocol/inspector@latest
  1. Open
    http://localhost:5173
  2. Enter MCP server URL
  3. Click "Connect"
  4. Use "List Tools" to see available tools
  5. Test tool calls with parameters
bash
npx @modelcontextprotocol/inspector@latest
  1. 打开
    http://localhost:5173
  2. 输入MCP服务器URL
  3. 点击“连接”
  4. 使用“列出工具”查看可用工具
  5. 带参数测试工具调用

Deploy to Cloudflare

部署到Cloudflare

bash
undefined
bash
undefined

First time: Login

首次部署:登录

npx wrangler login
npx wrangler login

Deploy

部署

npx wrangler deploy
npx wrangler deploy

Check deployment

查看部署日志

npx wrangler tail

**Your server is live at**:
undefined
npx wrangler tail

**你的服务器已上线**:
undefined

Connect Claude Desktop

连接Claude Desktop

~/.config/claude/claude_desktop_config.json (Linux/Mac):
json
{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp-server.your-account.workers.dev/sse"
    }
  }
}
%APPDATA%/Claude/claude_desktop_config.json (Windows)
With OAuth:
json
{
  "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"
      }
    }
  }
}
Restart Claude Desktop after config changes.

~/.config/claude/claude_desktop_config.json(Linux/Mac):
json
{
  "mcpServers": {
    "my-mcp": {
      "url": "https://my-mcp-server.your-account.workers.dev/sse"
    }
  }
}
%APPDATA%/Claude/claude_desktop_config.json(Windows)
带OAuth的配置:
json
{
  "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。

Common Patterns

常见模式

API Proxy MCP Server

API代理MCP服务器

Use case: Wrap external API with MCP tools
Pattern:
typescript
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
      }]
    };
  }
);
适用场景:用MCP工具包装外部API
模式:
typescript
this.server.tool(
  "search_wikipedia",
  "在维基百科中搜索主题",
  { 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
      }]
    };
  }
);

Database-Backed Tools

数据库驱动的工具

Use case: Query D1, KV, or external databases
Pattern:
typescript
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)
      }]
    };
  }
);
适用场景:查询D1、KV或外部数据库
模式:
typescript
this.server.tool(
  "get_user",
  "从数据库获取用户详情",
  { userId: z.string() },
  async ({ userId }) => {
    // 查询Durable Objects存储
    const user = await this.state.storage.get<User>(`user:${userId}`);

    // 或查询D1数据库
    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)
      }]
    };
  }
);

Multi-Tool Coordination

多工具协同

Use case: Tools that call other tools
Pattern:
typescript
// 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");
适用场景:工具之间互相调用
模式:
typescript
// 存储第一个工具的结果
await this.state.storage.put("last_search", result);

// 第二个工具读取该结果
const lastSearch = await this.state.storage.get("last_search");

Caching Strategy

缓存策略

Use case: Cache expensive API calls
Pattern:
typescript
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 }]
    };
  }
);
适用场景:缓存高成本API调用
模式:
typescript
this.server.tool(
  "get_weather",
  "获取天气信息(缓存5分钟)",
  { city: z.string() },
  async ({ city }) => {
    const cacheKey = `weather:${city}`;
    const cached = await this.state.storage.get<CachedWeather>(cacheKey);

    // 检查缓存是否新鲜
    if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
      return {
        content: [{ type: "text", text: cached.data }]
      };
    }

    // 获取最新数据
    const weather = await fetchWeatherAPI(city);

    // 缓存数据
    await this.state.storage.put(cacheKey, {
      data: weather,
      timestamp: Date.now()
    });

    return {
      content: [{ type: "text", text: weather }]
    };
  }
);

Rate Limiting with Durable Objects

基于Durable Objects的速率限制

Use case: Prevent abuse, respect upstream rate limits
Pattern:
typescript
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
  };
}

适用场景:防止滥用,遵守上游速率限制
模式:
typescript
async rateLimit(key: string, maxRequests: number, windowMs: number): Promise<boolean> {
  const now = Date.now();
  const requests = await this.state.storage.get<number[]>(`ratelimit:${key}`) || [];

  // 移除窗口外的旧请求
  const recentRequests = requests.filter(ts => now - ts < windowMs);

  if (recentRequests.length >= maxRequests) {
    return false; // 触发速率限制
  }

  // 添加当前请求
  recentRequests.push(now);
  await this.state.storage.put(`ratelimit:${key}`, recentRequests);

  return true; // 允许访问
}

// 在工具中使用
if (!await this.rateLimit(userId, 10, 60 * 1000)) {
  return {
    content: [{ type: "text", text: "速率限制已超出(每分钟10次请求)" }],
    isError: true
  };
}

15 Known Errors (With Solutions)

15种已知错误及解决方案

1. McpAgent Class Not Exported

1. McpAgent类未导出

Error:
TypeError: Cannot read properties of undefined (reading 'serve')
Cause: Forgot to export McpAgent class
Solution:
typescript
export class MyMCP extends McpAgent { ... }  // ✅ Must export
export default { fetch() { ... } }

错误信息:
TypeError: Cannot read properties of undefined (reading 'serve')
原因: 忘记导出McpAgent类
解决方案:
typescript
export class MyMCP extends McpAgent { ... }  // ✅ 必须导出
export default { fetch() { ... } }

2. Transport Mismatch

2. 传输协议不匹配

Error:
Connection failed: Unexpected response format
Cause: Client expects
/sse
but server only serves
/mcp
Solution: Serve both transports (see Transport Methods section)

错误信息:
Connection failed: Unexpected response format
原因: 客户端期望
/sse
端点,但服务器仅提供
/mcp
端点
解决方案: 同时提供两种传输协议(参考传输方法章节)

3. OAuth Redirect URI Mismatch

3. OAuth重定向URI不匹配

Error:
OAuth error: redirect_uri does not match
Cause: Client configured with localhost, but deployed to workers.dev
Solution: Update claude_desktop_config.json after deployment

错误信息:
OAuth error: redirect_uri does not match
原因: 客户端配置为localhost,但服务器已部署到workers.dev
解决方案: 部署后更新claude_desktop_config.json

4. WebSocket Hibernation State Loss

4. WebSocket休眠状态丢失

Error: Tool calls fail after reconnect with "state not found"
Cause: In-memory state cleared on hibernation
Solution: Use
this.state.storage
instead of instance properties

错误信息: 重新连接后工具调用失败,提示“state not found”
原因: 内存状态在休眠时被清除
解决方案: 使用
this.state.storage
替代实例属性

5. Durable Objects Binding Missing

5. Durable Objects绑定缺失

Error:
Error: Cannot read properties of undefined (reading 'idFromName')
Cause: Forgot DO binding in wrangler.jsonc
Solution: Add binding (see Stateful MCP Servers section)

错误信息:
Error: Cannot read properties of undefined (reading 'idFromName')
原因: 忘记在wrangler.jsonc中配置DO绑定
解决方案: 添加绑定(参考有状态MCP服务器章节)

6. Migration Not Defined

6. 未定义迁移

Error:
Error: Durable Object class MyMCP has no migration defined
Cause: First DO deployment requires migration
Solution:
jsonc
{
  "migrations": [
    { "tag": "v1", "new_classes": ["MyMCP"] }
  ]
}

错误信息:
Error: Durable Object class MyMCP has no migration defined
原因: 首次部署DO需要配置迁移
解决方案:
jsonc
{
  "migrations": [
    { "tag": "v1", "new_classes": ["MyMCP"] }
  ]
}

7. CORS Errors on Remote MCP

7. 远程MCP的CORS错误

Error:
Access to fetch at '...' blocked by CORS policy
Cause: MCP server doesn't return CORS headers
Solution: Use OAuthProvider (handles CORS) or add headers manually

错误信息:
Access to fetch at '...' blocked by CORS policy
原因: MCP服务器未返回CORS头
解决方案: 使用OAuthProvider(自动处理CORS)或手动添加头

8. Client Configuration Format Error

8. 客户端配置格式错误

Error: Claude Desktop doesn't recognize server
Cause: Wrong JSON format in claude_desktop_config.json
Solution: See "Connect Claude Desktop" section for correct format

错误信息: Claude Desktop无法识别服务器
原因: claude_desktop_config.json格式错误
解决方案: 参考“连接Claude Desktop”章节的正确格式

9. serializeAttachment() Not Used

9. 未使用serializeAttachment()

Error: WebSocket metadata lost on hibernation wake
Cause: Not using serializeAttachment()
Solution: See WebSocket Hibernation section

错误信息: WebSocket唤醒后元数据丢失
原因: 未使用serializeAttachment()保存元数据
解决方案: 参考WebSocket休眠章节

10. OAuth Consent Screen Disabled

10. OAuth授权界面已禁用

Security risk: Users don't see permissions
Cause:
allowConsentScreen: false
in production
Solution: Always set
allowConsentScreen: true
in production

安全风险: 用户看不到权限说明
原因: 生产环境中设置了
allowConsentScreen: false
解决方案: 生产环境中始终设置
allowConsentScreen: true

11. JWT Signing Key Missing

11. JWT签名密钥缺失

Error:
Error: JWT_SIGNING_KEY environment variable not set
Cause: OAuth Provider requires signing key
Solution:
bash
openssl rand -base64 32
错误信息:
Error: JWT_SIGNING_KEY environment variable not set
原因: OAuth Provider需要签名密钥
解决方案:
bash
openssl rand -base64 32

Add to wrangler.jsonc vars

添加到wrangler.jsonc的vars中


---

---

12. Environment Variables Not Configured

12. 环境变量未配置

Error:
env.MY_VAR is undefined
Cause: Variables in
.dev.vars
but not in wrangler.jsonc
Solution: Add to
"vars"
section in wrangler.jsonc

错误信息:
env.MY_VAR is undefined
原因: 变量仅在
.dev.vars
中配置,但未添加到wrangler.jsonc
解决方案: 添加到wrangler.jsonc的
"vars"
部分

13. Tool Schema Validation Error

13. 工具Schema验证错误

Error:
ZodError: Invalid input type
Cause: Client sends string, schema expects number
Solution: Use Zod transforms:
typescript
z.string().transform(val => parseInt(val, 10))

错误信息:
ZodError: Invalid input type
原因: 客户端发送字符串,但Schema期望数字
解决方案: 使用Zod转换:
typescript
z.string().transform(val => parseInt(val, 10))

14. Multiple Transport Endpoints Conflicting

14. 多个传输端点冲突

Error:
/sse
returns 404 after adding
/mcp
Cause: Incorrect path matching
Solution: Use
startsWith()
or exact matches

错误信息: 添加
/mcp
/sse
返回404
原因: 路径匹配逻辑错误
解决方案: 使用
startsWith()
或精确匹配

15. Local Testing with Miniflare Limitations

15. 使用Miniflare本地测试的局限性

Error: OAuth flow fails in local dev
Cause: Miniflare doesn't support all DO features
Solution: Use
npx wrangler dev --remote
for full DO support

错误信息: 本地开发中OAuth流程失败
原因: Miniflare不支持所有DO功能
解决方案: 使用
npx wrangler dev --remote
以获得完整DO支持

Configuration Reference

配置参考

Complete wrangler.jsonc (All Features)

完整wrangler.jsonc(包含所有功能)

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

Complete package.json

完整package.json

See
templates/package.json
参考
templates/package.json

Complete claude_desktop_config.json

完整claude_desktop_config.json

See
templates/claude_desktop_config.json

参考
templates/claude_desktop_config.json

Additional Resources

额外资源

Official Documentation

官方文档

Official Examples

官方示例

Tools

工具

When NOT to Use This Skill

不适用本技能的场景

Don't use this skill when:
  • Building Python MCP servers (use
    fastmcp
    skill instead)
  • Building local-only MCP servers (use
    typescript-mcp
    skill)
  • You need non-Cloudflare hosting (AWS Lambda, GCP, etc.)
  • You're working with Claude.ai web interface skills (different from MCP)
Use this skill specifically for: TypeScript + Cloudflare Workers + Remote MCP

请勿在以下场景使用本技能:
  • 构建Python MCP服务器(请使用
    fastmcp
    技能)
  • 构建仅本地运行的MCP服务器(请使用
    typescript-mcp
    技能)
  • 需要非Cloudflare托管(AWS Lambda、GCP等)
  • 开发Claude.ai网页版技能(与MCP不同)
本技能专门适用于: TypeScript + Cloudflare Workers + 远程MCP

Version Information

版本信息

  • @modelcontextprotocol/sdk: 1.21.0
  • @cloudflare/workers-oauth-provider: 0.0.13
  • agents (Cloudflare Agents SDK): 0.2.20
  • Last Verified: 2025-11-04
Production tested: Based on Cloudflare's official MCP servers (mcp-server-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 Efficiency

令牌效率

Without this skill:
  • Research scattered docs: ~10k tokens
  • Debug 15 errors: ~30k tokens
  • Total: ~40k tokens
With this skill:
  • Read skill: ~4k tokens
  • Copy templates: ~1k tokens
  • Total: ~5k tokens
Savings: ~87% (40k → 5k tokens)
Errors prevented: 15 (100% prevention rate)

Questions? Check:
  • references/authentication.md
    - Auth patterns comparison
  • references/transport.md
    - SSE vs HTTP technical details
  • references/oauth-providers.md
    - GitHub, Google, Azure setup
  • references/common-issues.md
    - Error troubleshooting deep-dives
  • references/official-examples.md
    - Curated links to Cloudflare examples
不使用本技能时:
  • 分散查阅文档: ~10k令牌
  • 调试15种错误: ~30k令牌
  • 总计: ~40k令牌
使用本技能时:
  • 阅读技能文档: ~4k令牌
  • 复制模板: ~1k令牌
  • 总计: ~5k令牌
节省比例: ~87%(从40k降至5k令牌)
避免的错误数量: 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精选示例链接