← back to docs
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.
Prerequisites
- 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.
1. Project setup
mkdir yoursite-mcp && cd yoursite-mcp pnpm dlx wrangler@latest init -y # replace src/index.js and wrangler.toml with the templates below
2.
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.
// The handler is the same shape as github.com/lmno100/agent-ready-kit-saas
// packages/mcp-server — copy modifications over time.
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" },
});
}
3.
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.
4. Deploy
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.
5. Re-scan
Run the scanner against your domain. Themcp-liveness check should now pass.
Don't want to maintain this?
Pro tier and above hosts the MCP server for you, with custom tool definitions backed by your business data. Every paid tier is consult-gated. See pricing or talk to a specialist.