Remote MCP servers are revolutionizing how AI agents access external tools and data. Cloudflare has become the first platform to support internet-accessible MCP servers with built-in OAuth authentication, moving us beyond local-only implementations to scalable, production-ready solutions that any AI assistant can access over the web.

The impact is significant - major companies like Atlassian, PayPal, Sentry, and Webflow have already deployed remote MCP servers on Cloudflare’s platform. With over 1,000 community-built MCP servers emerging in just three months since the protocol’s launch, the ecosystem is experiencing explosive growth, and remote deployment is quickly becoming the preferred approach for production use cases.

This guide will take you from zero to production with your first Cloudflare MCP server, covering everything from basic setup to enterprise-grade deployment patterns. We’ll build a practical document processing server that showcases Cloudflare’s unique remote MCP capabilities while demonstrating real-world patterns you can apply to your own projects.

Why remote MCP servers matter

Traditional MCP servers run locally via stdio transport - great for development, but limiting for production scenarios. Remote MCP servers unlock internet-scale AI agent capabilities by providing:

  • Universal accessibility: Any AI assistant can connect over HTTPS
  • Built-in authentication: OAuth 2.1 flows familiar to end users
  • Production scaling: Leverage Cloudflare’s global edge network
  • Stateful operations: Persistent SQL databases via Durable Objects
  • Enterprise security: Scoped permissions and token management

The shift from local to remote represents a fundamental evolution in how we architect AI-powered applications. Instead of requiring users to configure local servers, remote MCP servers provide plug-and-play experiences similar to traditional web APIs, but optimized specifically for AI agent interactions.

Cloudflare’s remote MCP advantages

Cloudflare’s platform offers several unique advantages for MCP server deployment:

Comprehensive transport support: Both Server-Sent Events (SSE) and the new Streamable HTTP transport with automatic transport negotiation. This ensures compatibility with different client implementations while providing optimal performance.

Built-in OAuth provider: Complete OAuth 2.1 implementation that handles authentication flows transparently. The platform supports GitHub, Google, Auth0, Stytch, WorkOS, and custom providers out of the box.

Global edge deployment: Your MCP server runs on Cloudflare’s global network, providing low latency worldwide. Workers automatically scale based on demand without requiring infrastructure management.

Stateful server support: Unlike stateless traditional APIs, MCP servers often need to maintain context between calls. Cloudflare’s Durable Objects provide persistent SQL databases that survive across sessions, enabling complex multi-step workflows.

Setting up your development environment

Let’s build a document processing MCP server that demonstrates these capabilities. Start by setting up your development environment:

Terminal window
# Create a new Cloudflare MCP server project
npm create cloudflare@latest -- document-processor --template=cloudflare/ai/demos/remote-mcp-server
cd document-processor
npm install

This creates a complete MCP server template with:

  • TypeScript configuration optimized for Workers
  • OAuth authentication scaffolding
  • SSE transport implementation
  • Local development server setup
  • Deployment configuration

The template includes Cloudflare’s McpAgent class, which abstracts the complexity of MCP protocol implementation while providing full access to Workers platform features.

Building your document processor

Open src/index.ts and replace the template code with our document processing implementation:

import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
// Document processing state
type DocumentState = {
processedFiles: Record<string, {
name: string;
processedAt: string;
extractedText?: string;
metadata?: any;
}>;
totalProcessed: number;
};
export class DocumentProcessor extends McpAgent<Env, DocumentState, {}> {
server = new McpServer({
name: "Document Processor",
version: "1.0.0",
});
initialState: DocumentState = {
processedFiles: {},
totalProcessed: 0,
};
async init() {
// Tool for extracting text from documents
this.server.tool(
"extract_text",
"Extract text content from document URLs",
{
url: z.string().url(),
format: z.enum(["plain", "markdown"]).optional().default("plain")
},
async ({ url, format }) => {
try {
// Fetch document content
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch document: ${response.status}`);
}
const content = await response.text();
const fileName = url.split('/').pop() || 'unknown';
// Process based on format
const extractedText = format === "markdown"
? this.convertToMarkdown(content)
: this.extractPlainText(content);
// Update state
const fileId = btoa(url).slice(0, 10);
this.setState({
processedFiles: {
...this.state.processedFiles,
[fileId]: {
name: fileName,
processedAt: new Date().toISOString(),
extractedText,
metadata: {
url,
format,
contentLength: content.length
}
}
},
totalProcessed: this.state.totalProcessed + 1
});
return {
content: [{
type: "text",
text: `Successfully extracted text from ${fileName}:\n\n${extractedText.slice(0, 500)}${extractedText.length > 500 ? '...' : ''}`
}],
isError: false,
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error processing document: ${error.message}`
}],
isError: true,
};
}
}
);
// Tool for listing processed documents
this.server.tool(
"list_processed",
"List all processed documents with metadata",
{},
async () => {
const files = Object.entries(this.state.processedFiles);
if (files.length === 0) {
return {
content: [{
type: "text",
text: "No documents have been processed yet."
}],
isError: false,
};
}
const fileList = files.map(([id, file]) =>
`• ${file.name} (processed: ${file.processedAt})`
).join('\n');
return {
content: [{
type: "text",
text: `Processed Documents (${this.state.totalProcessed} total):\n\n${fileList}`
}],
isError: false,
};
}
);
// Resource for accessing processed document content
this.server.resource(
"document://{fileId}",
"Access full content of processed documents",
async (fileId) => {
const file = this.state.processedFiles[fileId];
if (!file) {
throw new Error(`Document ${fileId} not found`);
}
return {
contents: [{
uri: `document://${fileId}`,
mimeType: "text/plain",
text: file.extractedText || "No content available"
}]
};
}
);
}
private extractPlainText(content: string): string {
// Basic HTML tag removal and text extraction
return content
.replace(/<[^>]*>/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.slice(0, 2000); // Limit to 2000 characters
}
private convertToMarkdown(content: string): string {
// Simple HTML to Markdown conversion
return content
.replace(/<h1[^>]*>(.*?)<\/h1>/gi, '# $1')
.replace(/<h2[^>]*>(.*?)<\/h2>/gi, '## $1')
.replace(/<h3[^>]*>(.*?)<\/h3>/gi, '### $1')
.replace(/<p[^>]*>(.*?)<\/p>/gi, '$1\n\n')
.replace(/<strong[^>]*>(.*?)<\/strong>/gi, '**$1**')
.replace(/<em[^>]*>(.*?)<\/em>/gi, '*$1*')
.replace(/<[^>]*>/g, '')
.replace(/\n\n+/g, '\n\n')
.trim()
.slice(0, 2000);
}
}

This implementation demonstrates several key patterns:

Stateful operations: The server maintains a record of processed documents using Durable Objects, enabling persistent state across sessions.

Multiple tool types: We provide both imperative tools (extract_text, list_processed) and declarative resources (document://), showcasing different interaction patterns.

Error handling: Comprehensive error handling with agent-friendly error messages that include actionable guidance.

Tool budget optimization: We limit tools to focused functionality rather than exposing every possible operation, following MCP best practices.

Adding OAuth authentication

Update your wrangler.toml to configure OAuth authentication:

name = "document-processor"
main = "src/index.ts"
compatibility_date = "2025-01-15"
[vars]
OAUTH_CLIENT_ID = "your-github-client-id"
OAUTH_REDIRECT_URI = "https://document-processor.your-subdomain.workers.dev/callback"
[[kv_namespaces]]
binding = "SESSIONS"
id = "your-kv-namespace-id"
preview_id = "your-preview-kv-namespace-id"

Create a GitHub OAuth application at https://github.com/settings/applications/new with:

Then set your secrets:

Terminal window
# Set OAuth credentials
npx wrangler secret put OAUTH_CLIENT_SECRET
# Enter your GitHub client secret when prompted
# Set session signing key
npx wrangler secret put SESSION_SECRET
# Enter a random 32-character string

Testing locally with MCP Inspector

Start your development server and test the implementation:

8787/sse
# Start local development server
npm start
# In another terminal, test with MCP Inspector
npx @modelcontextprotocol/inspector@latest http://localhost:8787/sse

The MCP Inspector provides a comprehensive testing interface where you can:

  • Verify server connection: Confirm your server starts correctly and responds to initialization
  • Test tool discovery: Ensure all tools are properly exposed with correct schemas
  • Execute tools: Test each tool with various inputs to verify functionality
  • Access resources: Validate resource templates and data retrieval
  • Debug errors: Identify and resolve issues before deployment

Test each tool thoroughly:

  1. extract_text with various document URLs
  2. list_processed after processing several documents
  3. document:// resources for retrieving processed content

Deploying to production

Deploy your MCP server to Cloudflare’s global network:

Terminal window
# Deploy to production
npx wrangler deploy
# Your server will be available at:
# https://document-processor.your-subdomain.workers.dev/sse

After deployment, configure your AI assistant to use the remote MCP server. For Claude Desktop, update your configuration file:

{
"mcpServers": {
"document-processor": {
"command": "npx",
"args": ["mcp-remote", "https://document-processor.your-subdomain.workers.dev/sse"]
}
}
}

The first time you use the server, you’ll be prompted to authenticate with GitHub through a standard OAuth flow. Once authenticated, the AI assistant can access your document processing tools seamlessly.

Production best practices

Tool budget management: Keep your server focused on 5-10 related tools maximum. Our document processor focuses specifically on document extraction and retrieval rather than trying to handle every possible document operation.

Error handling for agents: Provide specific, actionable error messages. Instead of “Access denied”, return “Authentication required. Visit https://your-server.workers.dev to authorize access with GitHub.”

State management: Use Durable Objects for persistent state that survives across sessions. Our processed documents remain available even after the server restarts.

Security scoping: Implement permission-based access control. Users only access documents they’ve personally processed through their authenticated session.

Performance optimization: Cache frequently accessed data and implement request batching where appropriate. For document processing, consider implementing result caching to avoid reprocessing identical documents.

Monitoring your deployment

Cloudflare provides comprehensive analytics for your MCP server through the Workers dashboard:

  • Request metrics: Monitor request volume, success rates, and error patterns
  • Performance data: Track execution time, memory usage, and global response times
  • Authentication flow: Monitor OAuth conversion rates and authentication failures
  • Resource usage: Track Durable Objects operations and KV namespace utilization

Set up alerts for high error rates or performance degradation to ensure reliable service for your AI agents.

Extending your MCP server

Our document processor provides a solid foundation for more advanced functionality:

Advanced document types: Add support for PDFs, Word documents, and other formats using Cloudflare’s PDF processing capabilities or external APIs.

Content analysis: Integrate with AI models to provide document summarization, keyword extraction, or sentiment analysis.

Batch processing: Implement tools for processing multiple documents simultaneously with progress tracking.

Integration APIs: Connect to document management systems like Google Drive, Dropbox, or SharePoint for seamless workflow integration.

Collaborative features: Enable document sharing and collaborative annotation through additional resources and tools.

Conclusion

Remote MCP servers represent the future of AI agent tool integration - moving from local development utilities to production-ready internet services. Cloudflare’s platform makes this transition seamless by providing OAuth authentication, global deployment, and stateful server capabilities out of the box.

The document processor we built demonstrates key patterns you can apply to any remote MCP server: stateful operations, comprehensive error handling, resource management, and production-ready authentication. These patterns scale from simple utility servers to complex enterprise integrations.

As the MCP ecosystem continues its explosive growth - with over 1,000 servers created in just three months - remote deployment on platforms like Cloudflare will become the standard approach for production AI agent tools. The combination of familiar OAuth flows, global edge deployment, and comprehensive developer tools positions remote MCP servers as the foundation for the next generation of AI-powered applications.

Start building your remote MCP servers today, and join the growing community of developers creating the tools that will power tomorrow’s AI agents.