Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Builds remote MCP (Model Context Protocol) servers on Cloudflare Workers with tools, OAuth authentication, and production deployment. Generates server code, configures auth providers, and deploys to Workers. Use when: user wants to \"build MCP server\", \"create MCP tools\", \"remote MCP\", \"deploy MCP\", add \"OAuth to MCP\", or mentions Model Context Protocol on Cloudflare. Also triggers on \"
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 23% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 39% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 6% | 0% |
Creates production-ready Model Context Protocol servers on Cloudflare Workers with tools, authentication, and deployment.
npm install -g wrangler)bashnpm 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
bashnpm 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.
Tools are functions MCP clients can call. Define them using server.tool():
typescriptimport { 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) }], }; } ); } }
Public server (src/index.ts):
typescriptimport { 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.
bash# Start server npm start # In another terminal, test with MCP Inspector npx @modelcontextprotocol/inspector@latest # Open http://localhost:5173, enter http://localhost:8788/mcp
bashnpx wrangler deploy
Server accessible at https://[worker-name].[account].workers.dev/mcp
Claude Desktop (claude_desktop_config.json):
json{ "mcpServers": { "my-server": { "command": "npx", "args": ["mcp-remote", "https://my-mcp.workers.dev/mcp"] } } }
Restart Claude Desktop after updating config.
typescript// 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) }, ], };
typescriptthis.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 } );
typescriptexport 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) }] }; }); } }
For OAuth-protected servers, see references/oauth-setup.md.
Supported providers:
Minimal wrangler.toml:
tomlname = "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.):
toml[[d1_databases]] binding = "DB" database_name = "my-db" database_id = "xxx" [[kv_namespaces]] binding = "KV" id = "xxx"
init() registers tools before connectionswrangler tail/mcpwrangler deployments listGITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET are sethttp://localhost:8788/callbackOther measured skills in the registry, with their headline benchmark lift.