Cloudflare Worker MCP
Free path to L5The kit's mcp/server-card.json declares/api/mcp as a callable JSON-RPC endpoint. This is a copy-paste Cloudflare Worker that implements it for you — five-minute deploy, no servers to run.
Using the generated Cloudflare delivery? That bundle's worker.js already implements /api/mcp with the full default tool suite live — you don't need this page. Use this hand-written Worker only to add /api/mcp onto a files-only deploy. To add custom tools either way, see Craft your own MCP tool.
- Your domain is on Cloudflare (DNS proxied through them).
- Wrangler installed:
pnpm dlx wrangler@latest --version - You've already deployed the kit's
.well-known/files.
mkdir yoursite-mcp && cd yoursite-mcp pnpm dlx wrangler@latest init -y # replace src/index.js and wrangler.toml with the templates below
src/index.js// Cloudflare Worker — implements MCP 2025-06-18 at /api/mcp on your domain.
// Bind a route in wrangler.toml: routes = [{ pattern = "yoursite.com/api/mcp/*", zone_name = "yoursite.com" }]
//
// Edit TOOLS below to match what you advertise in /.well-known/mcp/server-card.json.
const PROTOCOL_VERSION = "2025-06-18";
const SERVER_INFO = {
name: "yoursite-mcp", // <-- match mcp/server-card.json serverInfo.name
version: "1.0.0",
title: "Yoursite MCP Server",
};
const TOOLS = [
{
name: "get_business_info",
title: "Get Business Info",
description: "Return name, hours, contact, and a short summary of the business.",
inputSchema: { type: "object", properties: {} },
handler: async () => ({
name: "Yoursite",
hours: "Mon-Fri 9-5 PT",
phone: "+1 555 555 0100",
website: "https://yoursite.com",
summary: "What you do, in one sentence.",
}),
},
// Add more tools — book_appointment, get_quote, etc.
];
export default {
async fetch(request) {
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: corsHeaders(),
});
}
if (request.method !== "POST") {
return json({
name: SERVER_INFO.name,
note: "POST JSON-RPC 2.0 here. Methods: initialize, tools/list, tools/call.",
});
}
let body;
try {
body = await request.json();
} catch {
return json(rpcError(null, -32700, "Parse error"), 400);
}
const result = await handle(body);
return json(result);
},
};
async function handle(req) {
if (!req || req.jsonrpc !== "2.0" || typeof req.method !== "string") {
return rpcError(req?.id ?? null, -32600, "Not a valid JSON-RPC 2.0 request");
}
try {
switch (req.method) {
case "initialize":
return rpcOk(req.id, {
protocolVersion: PROTOCOL_VERSION,
serverInfo: SERVER_INFO,
capabilities: { tools: { listChanged: false } },
});
case "tools/list":
return rpcOk(req.id, {
tools: TOOLS.map(({ handler, ...t }) => t),
});
case "tools/call": {
const { name, arguments: args } = req.params || {};
const tool = TOOLS.find(t => t.name === name);
if (!tool) return rpcError(req.id, -32601, "Unknown tool: " + name);
const result = await tool.handler(args || {});
return rpcOk(req.id, {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
structuredContent: result,
isError: false,
});
}
case "ping":
return rpcOk(req.id, {});
default:
return rpcError(req.id, -32601, "Method not found: " + req.method);
}
} catch (err) {
return rpcError(req.id, -32603, err.message || String(err));
}
}
function rpcOk(id, result) { return { jsonrpc: "2.0", id: id ?? null, result }; }
function rpcError(id, code, message) { return { jsonrpc: "2.0", id: id ?? null, error: { code, message } }; }
function corsHeaders() {
return {
"access-control-allow-origin": "*",
"access-control-allow-methods": "GET, POST, OPTIONS",
"access-control-allow-headers": "content-type, accept",
};
}
function json(obj, status = 200) {
return new Response(JSON.stringify(obj), {
status,
headers: { ...corsHeaders(), "content-type": "application/json" },
});
}
wrangler.tomlname = "yoursite-mcp"
main = "src/index.js"
compatibility_date = "2025-01-01"
routes = [
{ pattern = "yoursite.com/api/mcp", zone_name = "yoursite.com" },
{ pattern = "yoursite.com/api/mcp/*", zone_name = "yoursite.com" },
]
Replace yoursite.com with your domain in both fields.
pnpm dlx wrangler deploy
Wrangler will prompt for Cloudflare auth on first run. After deploy, verify:
curl -X POST https://yoursite.com/api/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'Should return your tool list.
Run the scanner against your domain. Themcp-liveness check should now pass.
Don't want to maintain this?
The flat $450 package ships a live MCP for you (WordPress-plugin / Cloudflare-Worker delivery), and we build & wire custom tools to your data for $150 each. It closes in chat and delivers automatically the moment payment clears. See pricing or buy it in chat.