Skip to main content
Glama

mcp-worker-starter

A minimal Model Context Protocol server for Cloudflare Workers. Zero dependencies, one file, plain POST.

I run an MCP server in production that exposes live business data to Claude over authenticated tools. This is that server with the business logic taken out and the scar tissue left in.

The happy path of an MCP server is about forty lines. The parts worth publishing are the three traps below, because each one is silent, and one of them took down two of my products.


The 405 that costs you an outage

MCP clients open a GET with Accept: text/event-stream to listen for server-pushed messages. If your server does not speak SSE, the protocol says answer 405. That status is the signal for do not open this again.

I answered 200 with a friendly JSON body instead, because a 200 felt more helpful than an error.

The client read that 200 as a stream that had died, and reconnected. Immediately. With no backoff, and with no error surfaced anywhere I was looking.

201,936 requests in one day. It burned the daily request quota of the entire Cloudflare account, which took down a second, completely unrelated product that happened to share it. The MCP server itself never logged a single error, because from its side nothing was wrong. It was answering every request correctly, 201,936 times.

A 200 where the protocol expects 405 is not a friendlier answer. It is an infinite loop with good manners.

if ((request.headers.get("accept") ?? "").includes("text/event-stream")) {
  return new Response(JSON.stringify({ error: "This server does not expose an SSE stream. Use POST." }), {
    status: 405,
    headers: { "content-type": "application/json; charset=utf-8", allow: "POST" },
  });
}

Related MCP server: Remote MCP Server (Authless)

Notifications have no id, and must get no body

A JSON-RPC notification is fire-and-forget. It arrives without an id and the caller is not waiting for an answer. Reply with {"jsonrpc":"2.0","result":{}} and strict clients treat the exchange as malformed, because you answered something nobody asked.

202 with an empty body is the correct "received, nothing to say".

if (id === undefined || id === null) return new Response(null, { status: 202 });

Echo the client's protocolVersion

On initialize, send back the protocolVersion the client offered rather than hardcoding your own. Hardcoding gives you a handshake that works today and quietly stops working the week the client updates. Fall back to a default only when the client names none.


Use it

npm install
npx wrangler dev
curl -s http://localhost:8787 \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq

Deploy:

npx wrangler deploy

Then add the deployed URL as an MCP server in your client. It speaks POST.

Add your own tools

Edit the TOOLS array in src/index.ts. Two rules that matter more than they look:

  • description is the prompt. The model picks tools by reading it. Write it for a reader who cannot see your code and will not read the schema twice.

  • Return data, not prose. The model is better at describing your JSON than you are at guessing what it wants to say about it.

Auth and rate limiting

Both are off by default so the starter runs with no setup.

Token auth turns on when you set MCP_TOKEN. Requests must then present Authorization: Bearer <token>.

npx wrangler secret put MCP_TOKEN

Hourly rate limiting turns on when you bind a KV namespace as RATE_LIMIT. Default cap is 300 requests per hour. In production I cap per tenant rather than globally, keyed on whatever identifies the caller.

[[kv_namespaces]]
binding = "RATE_LIMIT"
id = "your-kv-namespace-id"

A rate limit is not paranoia here. Trap one is exactly the shape of failure a cap would have caught in minutes instead of a day.

What this is not

Not an SDK, not a framework, and not trying to be. If you want batteries, use the official TypeScript SDK or Cloudflare's Agents SDK.

This is for the case where you want to read the whole server in one sitting and know exactly what it does.

Tests

npm test

Covers the handshake, the tool round-trip, and each of the three traps, because a regression on any of them is invisible until it is expensive.

License

MIT

A
license - permissive license
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Allows deploying a Model Context Protocol server on Cloudflare Workers without authentication, enabling AI assistants to access custom tools through the MCP standard.

View all related MCP servers

Related MCP Connectors

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.

  • A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/andressalame/mcp-worker-starter'

If you have feedback or need assistance with the MCP directory API, please join our Discord server