building-cloudflare-mcp

Create MCP servers on Cloudflare Workers using the MCP Connector pattern. Use when user mentions "cloudflare mcp", "worker mcp", "mcp connector", or wants to deploy MCP tools on Cloudflare.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "building-cloudflare-mcp" with this command: npx skills add websmartteam/cor-code/websmartteam-cor-code-building-cloudflare-mcp

Cloudflare MCP Connector

Build and deploy MCP (Model Context Protocol) servers on Cloudflare Workers using the MCP Connector pattern.

Why Cloudflare Workers for MCP?

  • Global edge deployment - Low latency worldwide
  • MCP Connector compatible - Works with Claude API MCP integration
  • No cold starts - Always-on serverless
  • Free tier - 100K requests/day free
  • Easy authentication - Headers-based auth

Official Documentation References

When you need deeper context, fetch these with WebFetch:

Code Mode (Advanced Pattern)

For high-scale deployments with many tools, consider the Code Mode pattern:

  • Present MCP tools as code APIs (filesystem structure)
  • Agent writes code to call tools instead of direct tool calls
  • 98.7% token savings for large tool sets
  • Filter/transform data in execution environment before returning

Why it works: LLMs have seen millions of real TypeScript examples in training, but only contrived synthetic tool-call examples.

Cloudflare Agents SDK (built-in Code Mode):

import { codemode } from "agents/codemode/ai";

const {system, tools} = codemode({
  system: "You are a helpful assistant",
  tools: { /* tool definitions */ },
});

const stream = streamText({
  model: openai("gpt-5"),
  system,
  tools,
  messages: [{ role: "user", content: "..." }]
});

Docs: https://github.com/cloudflare/agents/blob/main/docs/codemode.md

MCP Server Structure

Minimal Worker Template

// src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // CORS preflight
    if (request.method === 'OPTIONS') {
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'POST, OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type, Authorization',
        },
      });
    }

    // Auth check
    const authHeader = request.headers.get('Authorization');
    if (authHeader !== `Bearer ${env.MCP_API_KEY}`) {
      return Response.json({ error: 'Unauthorized' }, { status: 401 });
    }

    // Parse MCP request
    const body = await request.json() as MCPRequest;

    // Handle MCP methods
    switch (body.method) {
      case 'tools/list':
        return Response.json({
          jsonrpc: '2.0',
          id: body.id,
          result: { tools: getToolsList() }
        });

      case 'tools/call':
        const result = await handleToolCall(body.params, env);
        return Response.json({
          jsonrpc: '2.0',
          id: body.id,
          result
        });

      default:
        return Response.json({
          jsonrpc: '2.0',
          id: body.id,
          error: { code: -32601, message: 'Method not found' }
        });
    }
  },
};

interface MCPRequest {
  jsonrpc: '2.0';
  id: string | number;
  method: string;
  params?: Record<string, unknown>;
}

interface Env {
  MCP_API_KEY: string;
  // Add other bindings (KV, D1, R2, etc.)
}

function getToolsList() {
  return [
    {
      name: 'example_tool',
      description: 'Description of what this tool does',
      inputSchema: {
        type: 'object',
        properties: {
          param1: { type: 'string', description: 'First parameter' },
        },
        required: ['param1'],
      },
    },
  ];
}

async function handleToolCall(params: { name: string; arguments: Record<string, unknown> }, env: Env) {
  switch (params.name) {
    case 'example_tool':
      return { content: [{ type: 'text', text: `Result: ${params.arguments.param1}` }] };
    default:
      throw new Error(`Unknown tool: ${params.name}`);
  }
}

wrangler.toml

name = "my-mcp-server"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[vars]
# Non-secret config here

# Secrets added via: wrangler secret put MCP_API_KEY

Claude Code Configuration

Add Remote MCP Server

# Using CLI (user scope for global access)
claude mcp add --scope user my-mcp-server \
  --transport http \
  --url "https://my-mcp-server.username.workers.dev" \
  --header "Authorization: Bearer YOUR_API_KEY"

Manual Configuration (~/.claude.json)

{
  "mcpServers": {
    "my-mcp-server": {
      "type": "http",
      "url": "https://my-mcp-server.username.workers.dev",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Auto-Approve Tools (~/.claude/settings.json)

{
  "autoApproveTools": [
    "mcp__my-mcp-server__*"
  ]
}

Tool Naming Convention

Tools from MCP servers follow this pattern:

mcp__<server-name>__<tool-name>

Examples:

  • mcp__my-mcp-server__example_tool
  • mcp__supabase__execute_sql
  • mcp__context7__query-docs

Authentication Patterns

Bearer Token (Recommended)

const authHeader = request.headers.get('Authorization');
if (authHeader !== `Bearer ${env.MCP_API_KEY}`) {
  return Response.json({ error: 'Unauthorized' }, { status: 401 });
}

API Key Header

const apiKey = request.headers.get('X-API-Key');
if (apiKey !== env.API_KEY) {
  return Response.json({ error: 'Unauthorized' }, { status: 401 });
}

IP Allowlist (Additional Layer)

const clientIP = request.headers.get('CF-Connecting-IP');
const allowedIPs = env.ALLOWED_IPS?.split(',') || [];
if (allowedIPs.length && !allowedIPs.includes(clientIP)) {
  return Response.json({ error: 'Forbidden' }, { status: 403 });
}

Deployment Workflow

# 1. Create project
npm create cloudflare@latest my-mcp-server -- --template worker-typescript

# 2. Install dependencies
cd my-mcp-server
npm install

# 3. Add secrets
wrangler secret put MCP_API_KEY
# Enter your secure API key

# 4. Deploy
wrangler deploy

# 5. Add to Claude Code
claude mcp add --scope user my-mcp-server \
  --transport http \
  --url "https://my-mcp-server.username.workers.dev" \
  --header "Authorization: Bearer YOUR_API_KEY"

# 6. Restart Claude Code
# Exit and run: claude --resume

Advanced: Cloudflare Bindings

KV Storage

// wrangler.toml
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"

// Usage
const value = await env.MY_KV.get('key');
await env.MY_KV.put('key', 'value');

D1 Database

// wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "abc123"

// Usage
const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?')
  .bind(userId)
  .first();

R2 Storage

// wrangler.toml
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"

// Usage
const object = await env.BUCKET.get('file.txt');
await env.BUCKET.put('file.txt', content);

Troubleshooting

MCP Not Loading

  1. Check worker is deployed: curl https://your-worker.workers.dev
  2. Verify auth header matches secret
  3. Restart Claude Code after config changes

Tools Not Appearing

  1. Check tools/list response format
  2. Verify tool schema is valid JSON Schema
  3. Check Claude Code logs: /mcp command

Permission Errors

  1. Add to autoApproveTools in settings.json
  2. Use wildcard: mcp__my-mcp-server__*

Security Checklist

  • Use strong, unique API keys (32+ chars)
  • Store secrets via wrangler secret put
  • Never commit secrets to git
  • Consider IP allowlisting for sensitive MCPs
  • Use HTTPS only (Cloudflare provides this)
  • Implement rate limiting if needed
  • Log access attempts for auditing

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

supabase-vercel-shop

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

stripe-shop-integration

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

openclaw-version-monitor

监控 OpenClaw GitHub 版本更新,获取最新版本发布说明,翻译成中文, 并推送到 Telegram 和 Feishu。用于:(1) 定时检查版本更新 (2) 推送版本更新通知 (3) 生成中文版发布说明

Archived SourceRecently Updated
Coding

ask-claude

Delegate a task to Claude Code CLI and immediately report the result back in chat. Supports persistent sessions with full context memory. Safe execution: no data exfiltration, no external calls, file operations confined to workspace. Use when the user asks to run Claude, delegate a coding task, continue a previous Claude session, or any task benefiting from Claude Code's tools (file editing, code analysis, bash, etc.).

Archived SourceRecently Updated