Skip to content

setkernel/cf-mcp-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cf-mcp-template

Production-ready Model Context Protocol (MCP) server on Cloudflare Workers. Bearer-token auth, structured logging, JSON-RPC 2.0 over HTTP. Stateless, edge-distributed, scale-to-zero. ~200 lines of TypeScript.

Companion to: MCP Servers in Production: When to Build, When to Skip

What it is

A working MCP server that:

  • Speaks JSON-RPC 2.0 over HTTP POST (the modern MCP transport).
  • Runs on Cloudflare Workers — globally distributed, scale-to-zero, no servers to manage.
  • Authenticates via bearer token (set as a Worker secret).
  • Exposes one sample tool — getCompanyFacts — that you replace with your own.

The boring production hygiene is already there: error handling with proper JSON-RPC error codes, request validation, structured logging, a health endpoint, and a landing page so visitors who hit the URL directly get something meaningful.

Setup (one-time, ~2 minutes)

# 1. Install
npm install

# 2. Pick a strong random token. This is the auth secret your MCP client will pass.
TOKEN=$(openssl rand -base64 32)
echo "Your MCP_TOKEN: $TOKEN"
echo "(save this — you'll paste it in your client config below)"

# 3. Set the token as a Worker secret
npx wrangler secret put MCP_TOKEN
# (paste the token from step 2)

# 4. Deploy
npm run deploy

The Worker is now live at https://cf-mcp-template.<your-subdomain>.workers.dev.

Test from curl

WORKER=https://cf-mcp-template.<your-subdomain>.workers.dev
TOKEN=<the token you saved above>

# Initialize
curl -s -X POST "$WORKER" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'

# List tools
curl -s -X POST "$WORKER" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# Call a tool
curl -s -X POST "$WORKER" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"getCompanyFacts","arguments":{"ticker":"NET"}}}'

Connect to Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the equivalent on your platform:

{
  "mcpServers": {
    "company-facts": {
      "url": "https://cf-mcp-template.<your-subdomain>.workers.dev",
      "headers": {
        "Authorization": "Bearer <your-MCP_TOKEN>"
      }
    }
  }
}

Restart Claude Desktop. The getCompanyFacts tool will now be available in conversations.

Connect to Cursor

In Cursor → Settings → MCP Servers → Add Server, paste:

{
  "url": "https://cf-mcp-template.<your-subdomain>.workers.dev",
  "headers": {
    "Authorization": "Bearer <your-MCP_TOKEN>"
  }
}

Add your own tools

Two places to edit in src/index.ts:

  1. TOOLS array — add a new entry with name, description, and a strict JSON Schema for inputSchema. The description is how the LLM knows when to call your tool — be specific.
  2. callTool function — add an if (name === 'yourToolName') { ... } branch. Validate inputs. Return { content: [{ type: 'text', text: '...' }] }.

For tools that hit external data (D1, KV, R2, third-party APIs):

  • Uncomment the relevant binding in wrangler.jsonc.
  • Add it to the Env interface in src/index.ts.
  • Pass env through to callTool (currently the function takes only name and args — extend the signature).

Production hardening checklist

The template is suitable for development and small-team use as-is. Before exposing to a wider audience, add:

  • Rate limiting. Configure Cloudflare's Rate Limiting Rules on your zone, scoped to this Worker's URL.
  • Per-tool caching. If a tool returns expensive-to-compute data that's cacheable, cache the result by name + args hash with a short TTL using Cloudflare KV or the Cache API.
  • Output size limits. Tool outputs that overflow the LLM's context window cause hallucinations. Pre-truncate or paginate.
  • Tool timeouts. Long-running tools should return a job ID + provide a getJobStatus tool for polling, not block.
  • Per-tool authz. If different clients need different tool access, add a permission map keyed by token rather than a single shared MCP_TOKEN.
  • Audit logging. Log every tool invocation with token-hash + tool-name + duration + success. Workers Logs is free.

What this isn't

  • Not stdio — the Worker speaks HTTP. For local stdio MCP servers, see Anthropic's official SDK.
  • Not OAuth-protected. Bearer token is sufficient for trusted internal use; switch to OAuth 2.1 (the MCP-spec preferred auth) for external developer audiences.
  • Not stateful. Each request is independent. For tools that need session state, add Cloudflare Durable Objects.

License

MIT — see LICENSE.

Built by

SetKernel Digital Inc. — a Cloudflare-native engineering studio. We design, build, and operate AI-augmented products on the edge. Need an MCP server architected for your specific tools? Write a brief.

About

Production MCP (Model Context Protocol) server on Cloudflare Workers. JSON-RPC 2.0, bearer auth, drop-in for Claude Desktop / Cursor. Companion to setkernel.com/blog/mcp-servers-in-production

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors