Claude-skill-registry building-mcp-server-on-cloudflare
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/building-mcp-server-on-cloudflare" ~/.claude/skills/majiayu000-claude-skill-registry-building-mcp-server-on-cloudflare && rm -rf "$T"
manifest:
skills/data/building-mcp-server-on-cloudflare/SKILL.mdsource content
Building MCP Servers on Cloudflare
Creates production-ready Model Context Protocol servers on Cloudflare Workers with tools, authentication, and deployment.
When to Use
- User wants to build a remote MCP server
- User needs to expose tools via MCP
- User asks about MCP authentication or OAuth
- User wants to deploy MCP to Cloudflare Workers
Prerequisites
- Cloudflare account with Workers enabled
- Node.js 18+ and npm/pnpm/yarn
- Wrangler CLI (
)npm install -g wrangler
Quick Start
Option 1: Public Server (No Auth)
npm create cloudflare@latest -- my-mcp-server \ --template=cloudflare/ai/demos/remote-mcp-authless cd my-mcp-server npm start
Server runs at
http://localhost:8788/mcp
Option 2: Authenticated Server (OAuth)
npm create cloudflare@latest -- my-mcp-server \ --template=cloudflare/ai/demos/remote-mcp-github-oauth cd my-mcp-server
Requires OAuth app setup. See references/oauth-setup.md.
Core Workflow
Step 1: Define Tools
Tools are functions MCP clients can call. Define them using
server.tool():
import { McpAgent } from "agents/mcp"; import { z } from "zod"; export class MyMCP extends McpAgent { server = new Server({ name: "my-mcp", version: "1.0.0" }); async init() { // Simple tool with parameters this.server.tool( "add", { a: z.number(), b: z.number() }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }], }) ); // Tool that calls external API this.server.tool( "get_weather", { city: z.string() }, async ({ city }) => { const response = await fetch(`https://api.weather.com/${city}`); const data = await response.json(); return { content: [{ type: "text", text: JSON.stringify(data) }], }; } ); } }
Step 2: Configure Entry Point
Public server (
src/index.ts):
import { MyMCP } from "./mcp"; export default { fetch(request: Request, env: Env, ctx: ExecutionContext) { const url = new URL(request.url); if (url.pathname === "/mcp") { return MyMCP.serveSSE("/mcp").fetch(request, env, ctx); } return new Response("MCP Server", { status: 200 }); }, }; export { MyMCP };
Authenticated server — See references/oauth-setup.md.
Step 3: Test Locally
# Start server npm start # In another terminal, test with MCP Inspector npx @modelcontextprotocol/inspector@latest # Open http://localhost:5173, enter http://localhost:8788/mcp
Step 4: Deploy
npx wrangler deploy
Server accessible at
https://[worker-name].[account].workers.dev/mcp
Step 5: Connect Clients
Claude Desktop (
claude_desktop_config.json):
{ "mcpServers": { "my-server": { "command": "npx", "args": ["mcp-remote", "https://my-mcp.workers.dev/mcp"] } } }
Restart Claude Desktop after updating config.
Tool Patterns
Return Types
// Text response return { content: [{ type: "text", text: "result" }] }; // Multiple content items return { content: [ { type: "text", text: "Here's the data:" }, { type: "text", text: JSON.stringify(data, null, 2) }, ], };
Input Validation with Zod
this.server.tool( "create_user", { email: z.string().email(), name: z.string().min(1).max(100), role: z.enum(["admin", "user", "guest"]), age: z.number().int().min(0).optional(), }, async (params) => { // params are fully typed and validated } );
Accessing Environment/Bindings
export class MyMCP extends McpAgent<Env> { async init() { this.server.tool("query_db", { sql: z.string() }, async ({ sql }) => { // Access D1 binding const result = await this.env.DB.prepare(sql).all(); return { content: [{ type: "text", text: JSON.stringify(result) }] }; }); } }
Authentication
For OAuth-protected servers, see references/oauth-setup.md.
Supported providers:
- GitHub
- Auth0
- Stytch
- WorkOS
- Any OAuth 2.0 compliant provider
Wrangler Configuration
Minimal
wrangler.toml:
name = "my-mcp-server" main = "src/index.ts" compatibility_date = "2024-12-01" [durable_objects] bindings = [{ name = "MCP", class_name = "MyMCP" }] [[migrations]] tag = "v1" new_classes = ["MyMCP"]
With bindings (D1, KV, etc.):
[[d1_databases]] binding = "DB" database_name = "my-db" database_id = "xxx" [[kv_namespaces]] binding = "KV" id = "xxx"
Common Issues
"Tool not found" in Client
- Verify tool name matches exactly (case-sensitive)
- Ensure
registers tools before connectionsinit() - Check server logs:
wrangler tail
Connection Fails
- Confirm endpoint path is
/mcp - Check CORS if browser-based client
- Verify Worker is deployed:
wrangler deployments list
OAuth Redirect Errors
- Callback URL must match OAuth app config exactly
- Check
andGITHUB_CLIENT_ID
are setGITHUB_CLIENT_SECRET - For local dev, use
http://localhost:8788/callback
References
- references/examples.md — Official templates and production examples
- references/oauth-setup.md — OAuth provider configuration
- references/tool-patterns.md — Advanced tool examples
- references/troubleshooting.md — Error codes and fixes