How to Deploy an MCP Server on Cloudflare Workers
TL;DR: Use Cloudflare's MCP template (npm create cloudflare -- --template=cloudflare/ai/demos/remote-mcp-github-oauth), deploy with wrangler deploy. Get global edge distribution and OAuth built-in. Or skip configuration entirely with MCPize.
I spent way too long figuring out remote MCP deployment last year. Local servers were easy. Just spin up a process, connect via stdio, done. But getting an MCP server running on the edge? That was a different story.
Then Cloudflare dropped their MCP support in March 2025 and everything changed. When you deploy MCP on Cloudflare, you get global edge distribution, OAuth authentication baked in, and WebSocket hibernation that keeps costs near zero. Your Cloudflare MCP server sleeps when nobody's using it and wakes up in milliseconds when called.
This guide walks you through the complete workflow to deploy MCP on Cloudflare Workers. We'll go from zero to a fully authenticated Cloudflare MCP server that Claude Desktop, Cursor, and Windsurf can connect to from anywhere. Takes about 30-45 minutes if you're following along.
Or here's the thing. If you don't want to deal with wrangler configs and OAuth setup, you could skip all this and deploy on MCPize instead. One click. Done.
Skip the DevOps? Deploy on MCPize insteadWhat Is a Remote MCP Server?#
Local MCP servers run as subprocesses on your machine. They communicate via stdio. Super simple for personal use. But they have some obvious limitations.
Your computer needs to be running. Sharing with teammates means everyone has to run the server locally. And if you're building something that needs to scale, stdio ain't gonna cut it.
Remote MCP servers solve all of this. When you deploy MCP to Cloudflare, your server exposes tools over HTTP. Any MCP client can connect from anywhere. Your laptop can be off. Your teammates can use the same server. It just works.
Cloudflare Workers MCP uses Streamable HTTP transport. That's the current MCP standard for remote connections. You might also see references to SSE (Server-Sent Events) transport. That's deprecated but still works if you need backwards compatibility with older clients.
The transport stuff matters because when you connect Claude Desktop to your Cloudflare MCP server, you're going to use the /mcp endpoint for Streamable HTTP. The /sse endpoint is there for legacy support.
Prerequisites for Cloudflare MCP Deployment#
Before we deploy MCP on Cloudflare, make sure you have these ready:
- Cloudflare account (free tier works fine for development)
- Node.js 18+ installed on your machine
- Wrangler CLI installed globally:
npm install -g wrangler - Basic TypeScript knowledge (we're writing TypeScript here)
- GitHub account (optional, for OAuth setup later)
If you haven't built an MCP server before, you might want to check out the build guide first. This tutorial assumes you understand the basics of MCP tools and resources.
Log into Cloudflare from your terminal:
wrangler login
This opens your browser for authentication. Once you're logged in, we're ready to scaffold a project.
Step 1: Create Your Cloudflare MCP Server Project#
Cloudflare provides templates that scaffold everything you need. The fastest way to deploy MCP on Cloudflare is to use their official remote MCP template.
Option A: Authless Template (For Development)#
For quick testing without authentication:
npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-authless
This creates a project with:
src/index.tswith the main server entry pointwrangler.tomlfor Cloudflare configuration- Pre-configured MCP transport handling
Navigate into your project:
cd my-mcp-server
npm install
Option B: OAuth Template (For Production)#
For production Cloudflare MCP servers, you want authentication from day one. Use the GitHub OAuth template:
npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-github-oauth
This template includes everything from Option A plus the OAuth plumbing we'll configure in Step 4.
Project Structure#
After scaffolding, your project looks like this:
my-mcp-server/
├── src/
│ └── index.ts # Your MCP server code
├── wrangler.toml # Cloudflare config
├── package.json
└── tsconfig.json
The magic happens in src/index.ts. That's where you define your MCP tools and resources.
Step 2: Define Your MCP Tools and Resources#
Open src/index.ts. The template includes an example tool. Let's replace it with something more useful.
Creating Tools with McpAgent#
Cloudflare's Agents SDK gives you the McpAgent class. This handles all the transport complexity. You just focus on your tool logic.
import { McpAgent } from "cloudflare:agents/mcp";
import { z } from "zod";
interface Env {
// Your environment bindings go here
}
export class MyMCP extends McpAgent<Env> {
async init() {
// Define a tool for searching documentation
this.server.tool(
"search_docs",
{ query: z.string().describe("Search query") },
async ({ query }) => {
const results = await this.searchDocumentation(query);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
};
}
);
// Define a tool for getting user info
this.server.tool(
"get_user",
{ userId: z.string().describe("User ID to look up") },
async ({ userId }) => {
const user = await this.fetchUser(userId);
return {
content: [{ type: "text", text: JSON.stringify(user, null, 2) }]
};
}
);
}
private async searchDocumentation(query: string) {
// Your search logic here
return [{ title: "Result 1", url: "https://example.com" }];
}
private async fetchUser(userId: string) {
// Your user lookup logic here
return { id: userId, name: "Example User" };
}
}
The z import is from Zod. You use it to define input schemas for your tools. The MCP protocol requires schema definitions so clients know what parameters to send.
Adding Resources#
Resources are read-only data your MCP server exposes. Think of them as GET endpoints that clients can query:
this.server.resource(
"config://app/settings",
"Application configuration",
async () => {
return {
contents: [{
uri: "config://app/settings",
mimeType: "application/json",
text: JSON.stringify({ theme: "dark", language: "en" })
}]
};
}
);
Keep your tools focused. Each tool should do one thing well. Claude and other LLMs work better with specific, well-named tools than with Swiss Army knife functions that try to do everything.
For more examples, check out the MCP server examples page or browse the TypeScript MCP guide.
Step 3: Deploy MCP to Cloudflare Workers#
Time to get this thing running. First, test locally.
Local Development#
Start the development server:
npx wrangler dev
Your Cloudflare MCP server is now running at http://localhost:8787. Test it with the MCP Inspector:
npx @modelcontextprotocol/inspector http://localhost:8787/mcp
The Inspector opens a web UI where you can call your tools and see responses. Super useful for debugging before you deploy MCP to Cloudflare production.
Play around with your tools. Make sure they return the expected responses. Fix any bugs now because debugging on production is always more painful.
Production Deployment#
Once everything works locally, deploy:
npx wrangler deploy
Wrangler builds your project and pushes it to Cloudflare's edge network. You'll see output like this:
Uploaded my-mcp-server (1.2 MB)
Published my-mcp-server
https://my-mcp-server.your-account.workers.dev
That URL is your production Cloudflare MCP server. It's live. Globally distributed. Ready to handle requests from anywhere in the world.
Environment Variables#
For production secrets (API keys, database URLs, etc.), use Wrangler secrets:
npx wrangler secret put API_KEY
This prompts for the value and stores it encrypted. Access it in your code via env.API_KEY.
Step 4: Add OAuth Authentication to Your Cloudflare MCP Server#
Running an unauthenticated MCP server on the public internet is a bad idea. Anyone can hit your endpoints. Let's lock it down with OAuth.
Cloudflare provides @cloudflare/workers-oauth-provider for turnkey OAuth 2.1 support. This library handles token issuance, refresh, and scope management. You configure the provider, it does the heavy lifting.
Setting Up GitHub OAuth#
First, create a GitHub OAuth App:
- Go to github.com/settings/developers
- Click "New OAuth App"
- Set the callback URL to
https://your-server.workers.dev/callback - Save the Client ID and Client Secret
Add those as Wrangler secrets:
npx wrangler secret put GITHUB_CLIENT_ID
npx wrangler secret put GITHUB_CLIENT_SECRET
Create a KV namespace for token storage:
npx wrangler kv namespace create "OAUTH_KV"
This outputs a namespace ID. Add it to your wrangler.toml:
[[kv_namespaces]]
binding = "OAUTH_KV"
id = "your-namespace-id-here"
Configuring the OAuth Provider#
The OAuth template already has most of this wired up. Here's what the core configuration looks like:
import { OAuthProvider } from "@cloudflare/workers-oauth-provider";
export default new OAuthProvider({
apiRoute: ["/mcp/"],
tokenEndpoint: "https://your-server.workers.dev/oauth/token",
storage: {
kv: "OAUTH_KV"
},
async startAuthorization(request, env) {
const gh = new URL("https://github.com/login/oauth/authorize");
gh.searchParams.set("client_id", env.GITHUB_CLIENT_ID);
gh.searchParams.set("redirect_uri", "https://your-server.workers.dev/callback");
gh.searchParams.set("scope", "read:user user:email");
return Response.redirect(gh.toString(), 302);
}
});
The library stores only hashes of secrets in KV, not raw values. Security is baked in.
Alternative OAuth Providers#
GitHub isn't your only option. The workers-oauth-provider supports multiple providers:
- Auth0 works great for enterprise deployments with detailed audit logs
- Stytch handles modern passwordless authentication
- Google is familiar to most users
- WorkOS gives you enterprise SSO with SAML and SCIM
The setup pattern is the same. Create an OAuth app with the provider, configure the redirect URL, add secrets to Wrangler, and wire up the authorization flow.
Step 5: Connect MCP Clients to Your Cloudflare MCP Server#
Your Cloudflare MCP server is deployed and authenticated. Now let's connect some clients.
Claude Desktop Configuration#
Claude Desktop doesn't natively support remote HTTP connections yet. It uses stdio. But there's a bridge called mcp-remote that translates between the two.
Find your Claude Desktop config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add your remote server:
{
"mcpServers": {
"my-cloudflare-server": {
"command": "npx",
"args": [
"mcp-remote",
"https://my-mcp-server.workers.dev/mcp"
]
}
}
}
Save and restart Claude Desktop completely. Look for the hammer icon in the input box. Your tools should appear there.
If you're using OAuth, mcp-remote handles the authentication flow. It opens a browser window for login and caches the tokens locally.
Cursor IDE Setup#
Cursor has native remote MCP support. No adapter needed.
- Open Settings (Cmd+,)
- Go to MCP settings
- Add a new server
- Paste your URL:
https://my-mcp-server.workers.dev/mcp
Cursor handles the rest. Your tools show up in the AI panel.
Windsurf Integration#
Windsurf also supports remote MCP natively. Open the MCP configuration panel and add your /mcp endpoint URL directly.
Testing Your Connection#
Ask Claude or your IDE to use a tool:
"Use the search_docs tool to find information about authentication"
If everything is wired up correctly, you'll see the tool call and response in the conversation.
Testing and Debugging Your Cloudflare MCP Server#
Things will break. Here's how to fix them.
MCP Inspector#
The Inspector is your best friend:
npx @modelcontextprotocol/inspector https://your-server.workers.dev/mcp
It shows exactly what's happening with each request. Tool calls, responses, errors. Everything.
Wrangler Logs#
For production debugging, tail your logs:
npx wrangler tail
This streams real-time logs from your Cloudflare MCP server. You'll see every request, every error, every console.log.
Common Error Patterns#
Watch for these:
- CORS errors: The MCP SDK handles CORS automatically. If you see CORS issues, something's wrong with your routing.
- 401 Unauthorized: OAuth tokens expired or missing. Check your callback URL configuration.
- 500 errors: Something threw in your tool handler. Check the logs.
Deploy MCP on Cloudflare vs MCPize: When to Use Each#
Both platforms deploy MCP servers. They optimize for different goals.
When you deploy MCP on Cloudflare, you get full control over everything. You configure the OAuth. You manage the KV namespaces. You handle the monitoring. Total flexibility.
MCPize takes a different approach. Everything is managed. No wrangler configs. No OAuth setup. One-click deployment.
| Feature | Cloudflare Workers | MCPize |
|---|---|---|
| Setup time | 30-60 minutes | 2 minutes |
| OAuth setup | Manual configuration | Built-in |
| Monitoring | DIY with Workers Analytics | Included |
| Marketplace discovery | None | 10K+ developers |
| Monetization | Build it yourself | 85% revenue share |
| Best for | Full control, custom auth | Quick launch, earning revenue |
When to Choose Cloudflare#
You want Cloudflare when:
- You need full infrastructure control
- You have existing Cloudflare Workers setup
- Custom authentication requirements are a must
- You want to integrate with other Cloudflare products (R2, D1, Durable Objects)
When to Choose MCPize#
You want MCPize when:
- Speed matters more than customization
- You want built-in monetization from day one
- DevOps isn't your focus
- You'd rather spend time building features than infrastructure
Common Issues and Troubleshooting#
Here's a quick reference for the problems I see most often:
| Issue | Cause | Solution |
|---|---|---|
| CORS errors | Missing headers or routing issue | Cloudflare Workers MCP SDK handles CORS. Check your routes. |
| OAuth redirect fails | Wrong callback URL | Verify URL matches exactly in GitHub OAuth settings |
| "Transport not supported" | Client expects stdio | Use mcp-remote adapter for Claude Desktop |
| KV namespace error | Missing or wrong binding | Check wrangler.toml has correct namespace ID |
| High cold start latency | Large bundle size | Use dynamic imports for heavy dependencies |
| Rate limiting | Too many requests | Implement caching or upgrade to paid plan |
Debugging OAuth Issues#
OAuth is where most people get stuck. Double-check:
- Callback URL matches exactly (including trailing slashes)
- Client ID and Secret are set correctly
- KV namespace is bound in wrangler.toml
- Scopes match what your app needs
FAQ#
Can I deploy MCP on Cloudflare for free?
Yes. The Cloudflare Workers free tier includes 100,000 requests per day and 10ms CPU time per invocation. That's plenty for development and light production use. If you need Durable Objects for stateful Cloudflare MCP servers, the paid plan starts at $5/month.
What's the difference between /mcp and /sse endpoints?
The /mcp endpoint uses Streamable HTTP transport. That's the current recommended standard for Cloudflare Workers MCP. The /sse endpoint uses Server-Sent Events, which is deprecated but still supported for backwards compatibility with older clients.
How do I secure my Cloudflare MCP server?
Use @cloudflare/workers-oauth-provider for OAuth 2.1 authentication. The library handles token management, authorization flows, and secure storage. Never deploy an unauthenticated MCP server to production.
Do I need Durable Objects to deploy MCP on Cloudflare?
For stateless tools, no. Your Cloudflare MCP server can run on plain Workers. For stateful servers that need to remember context between calls (conversation history, user preferences), the McpAgent class uses Durable Objects automatically. The free tier now includes Durable Objects with 100,000 requests per day.
What MCP clients support Cloudflare MCP servers?
Claude Desktop (via mcp-remote adapter), Cursor IDE, Windsurf, and Cloudflare's AI Playground all support remote Cloudflare MCP server connections. More clients are adding native remote support throughout 2025.
Is there an easier alternative to Cloudflare for MCP hosting?
Yes. MCPize hosting offers one-click MCP server deployment with built-in OAuth, monitoring, and automatic updates. No Cloudflare account or wrangler configuration required. Plus you can monetize your server from day one.
Next Steps After You Deploy MCP on Cloudflare#
Your Cloudflare MCP server is deployed and authenticated. Here's what to do next:
- Add monitoring with Workers Analytics to track usage patterns
- Implement rate limiting to protect against abuse
- Explore hibernation since McpAgent supports WebSocket hibernation by default
- Publish to MCPize to reach 10K+ developers and earn 85% revenue share
If you want to build more servers, check out the Python MCP tutorial for a different language or browse the MCP marketplace to see what others are building.
Build Your MCP Server Browse MCP ServersRelated:
- Build MCP Server - Complete development guide
- Deploy with Docker - Containerization alternative
- Publish MCP Server - Marketplace publishing guide
- What is MCP? - MCP protocol explained
Need help? Join MCPize Discord or check the Cloudflare Agents documentation.



