Cloudflare Agents + MCP on the Free Tier: The Cheapest Way to Ship a Production Agent
Cloudflare Agents SDK now supports MCP clients and Durable Objects on the free tier. Build and deploy a production AI agent at the edge without a Kubernetes bill.
Agents SDK Architecture: Workers + Durable Objects + MCP Client/Server
Understanding how the pieces fit together is crucial for effective implementation:
☁️ Cloudflare Workers (Compute)
The execution environment for your agent logic — lightweight, globally distributed JavaScript/TypeScript runtime that handles HTTP requests, executes your agent code, and scales to zero when idle.
💾 Durable Objects (State)
Single-instance, strongly consistent objects that provide durable storage for agent state, conversation history, and context. Each DO runs on a single Cloudflare worker but provides ACID transaction guarantees.
🔌 MCP Client/Server (Integration)
The Models Client/Server protocol implementation that lets your agent communicate with external services (SAP, GitHub, databases) or host MCP servers for other agents to connect to.
The power comes from how these layers interact: Your Worker receives a request, uses an MCP client to query external services via Durable Object-stored context, performs reasoning, and returns results — all with sub-second global latency.
Building Your MCP Server: Two Approaches
When creating an agent that exposes capabilities to others, you have two primary patterns:
Option 1: MCP Agent (Stateful, Long-Running Context)
Best for agents that need to maintain conversation state or accumulate knowledge over time:
import { McpAgent } from "agents";
// Your agent inherits from McpAgent, gaining built-in MCP server capabilities
class MyAgent extends McpAgent {
async onRequest(req: Request): Promise {
// Handle MCP requests (tools, resources, prompts)
return super.onRequest(req);
}
// Optional: Handle custom HTTP endpoints alongside MCP
async fetch(req: Request): Promise {
if (req.method === "POST" && new URL(req.url).pathname === "/custom") {
// Custom logic here
return new Response(JSON.stringify({ result: "custom" }));
}
return await this.onRequest(req);
}
}
Option 2: Stateless MCP Handler (Request-Response)
Ideal for simple, idempotent operations where state isn't needed between requests:
import { createMcpHandler } from "agents";
// Create a standalone MCP handler that can be mounted in any Worker
const mcpHandler = createMcpHandler({
tools: {
// Define your tools here
"read_customer_data": {
description: "Retrieve customer information by ID",
parameters: {
type: "object",
properties: {
customerId: { type: "string" }
},
required: ["customerId"]
},
execute: async ({ customerId }) => {
// Implement your logic here
return { success: true, data: {} };
}
}
}
});
export default {
async fetch(request, env, ctx): Promise {
// Route to MCP handler for /mcp endpoint
if (new URL(request.url).pathname.startsWith("/mcp")) {
return await mcpHandler.handle(request);
}
// Handle other routes
return new Response("Hello World");
}
};
Choose the MCP Agent pattern when you need state persistence (conversation history, user preferences, cached data). Choose the Stateless Handler for simple microservices or when integrating with external state stores like D1 or KV.
Free Tier Limits: Know Your Boundaries
Cloudflare's free tier is generous but not infinite — understand the constraints to avoid surprises:
Workers (Compute)
- 100,000 requests per day
- 10 MB total memory storage
- 50ms CPU time per request (average)
- 30 second maximum request duration
- 30 unique Workers scripts
Durable Objects (State)
- 1,000 Durable Objects
- 128 MB total storage across all DOs
- 1,000 reads/writes per day per DO
- 10 second maximum request duration for DO calls
Additional Considerations
- No custom domains on free tier (use
*.workers.devsubdomain) - Limited diagnostic tools (no detailed logs or tracing)
- No guaranteed uptime SLA
- Rate limiting may apply during peak usage periods
Pro tip: Monitor your usage via the Cloudflare dashboard. For production workloads approaching limits, consider upgrading to the $5/month Paid tier which offers 10x increases in most limits.
OAuth Patterns for Enterprise Tool Access
Connecting to business-critical tools requires secure authentication — here's how to implement it properly:
The OAuth 2.0 Dance (Simplified):
- User initiates connection to service (e.g., "Connect my GitHub account")
- Your agent redirects to provider's authorization endpoint
- User authenticates and grants permissions
- Provider redirects back with authorization code
- Your agent exchanges code for access/refresh tokens
- Store tokens securely (encrypted in Durable Object or KV)
- Use access token for API calls; refresh when expired
Implementation Best Practices:
- Use established libraries: Don't roll your own OAuth — use
@node-oauth/oauth2-clientor similar - Secure token storage: Never store raw tokens — encrypt them with a key stored in environment variables
- Token rotation: Implement automatic refresh before expiration to avoid seamless user Experience
- Scope minimization: Request only the permissions you actually need
- Error handling: Gracefully handle token revocation, expired credentials, and API rate limits
Popular Provider Patterns:
GitHub
Scope: repo for full access, repo:status for status checks only. Store token encrypted in Durable Object.
Salesforce
Use OAuth 2.0 JWT Bearer flow for server-to-server if possible. Otherwise standard auth code flow with refresh tokens.
SAP Systems
OAuth 2.0 with SAML Bearer assertion or client credentials flow depending on your landscape setup.
Code Mode: Why Executing Beats Dumping
One of the most underappreciated features in the Agents SDK is Code Mode — a game-changer for agent effectiveness:
The Problem with Tool Dumping:
Traditional agents expose every possible function as a separate tool. An agent with access to a SAP system might have 200+ tools — overwhelming the LLM's context window and making tool selection unreliable.
The Code Mode Solution:
Instead of exposing individual functions, expose a single execute_code tool that allows the agent to write and run JavaScript/TypeScript in a sandboxed environment:
{
"name": "execute_code",
"description": "Execute JavaScript code in a secure sandbox to accomplish tasks",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The code to execute"
}
},
"required": ["code"]
},
"execute": async ({ code }) => {
// Create isolated VM context
const ctx = { /* your safe APIs here */ };
// Execute with timeout and resource limits
try {
const result = await compileAndRun(code, ctx, { timeout: 5000 });
return { success: true, output: result };
} catch (error) {
return { success: false, error: error.message };
}
}
}
Benefits of Code Mode:
- Context efficiency: One tool definition vs. hundreds
- Flexibility: Agents can compose operations dynamically
- Discoverability: No need to predict every possible operation in advance
- Maintenance: Update backend logic without changing tool definitions
- Security: Sandboxed execution with explicit boundaries
For SAP integration, this might mean exposing a single execute_abap_operation tool that lets the agent construct and run complex database queries or function calls through a safe abstraction layer.
ayraix.com Relevance: Pages + Workers + Agent Endpoints
Here's how this applies to our own platform:
Current Architecture:
- Frontend: Cloudflare Pages (static site hosting)
- Backend/API: Cloudflare Workers (serverless functions)
- Data: D1 (SQLite) for structured data, KV for blobs/cache
- Search: Recently integrated with external search provider
Agent Integration Opportunities:
- Content agent: MCP-exposed tools for article retrieval, tag suggestions, related content discovery
- Moderation agent: Automated first-pass content screening using MCP to access historical decisions
- Personalization agent: User-specific content recommendations based on reading history
- SEO agent: Automated meta description generation, keyword suggestions, internal linking recommendations
- Analytics agent: Natural language querying of site traffic patterns
Implementation Approach:
- Expose existing API endpoints as MCP tools via
createMcpHandler() - Create dedicated Worker for agent functionality
- Use Durable Objects for agent state and conversation history
- Implement OAuth for connections to external services (GitHub for content sourcing, etc.)
- Leverage Code Mode for complex data processing tasks
The beauty is that much of this could be prototyped and tested entirely on the free tier before considering any infrastructure investment.
Deploy Checklist: From Localhost to Global Edge
Follow this checklist to ensure your agent is production-ready:
-
Secrets Management
- Store all API keys and tokens in wrangler.toml secrets (never in code)
- Use different secrets profiles for preview/production environments
- Validate secret access in wrangler dev before deploying
-
CF Access Headers (If Applicable)
- If protecting with Cloudflare Access, verify JWT validation logic
- Check for proper audience (aud) and issuer (iss) claims
- Test with both authenticated and unauthenticated requests
-
Observability
- Implement structured logging (JSON format for easy parsing)
- Add request ID tracing for distributed debugging
- Monitor key metrics: request duration, error rates, memory usage
- Set up alerts for anomalous patterns (sudden error spikes, latency increases)
-
Performance Optimization
- Minify JavaScript and enable compression
- Use async/await properly to avoid blocking the event loop
- Cache frequently accessed data appropriately (consider KV for read-heavy workloads)
- Batch external API calls where possible
- Leverage Cloudflare's built-in caching for static assets
-
Security Hardening
- Validate and sanitize all inputs (especially if executing user-provided code)
- Implement rate limiting to prevent abuse
- Use Content Security Policy (CSP) headers where applicable
- Regular dependency audits (npm audit or equivalent)
- Consider integrating with Cloudflare's Bot Management or Custom Rules for threat mitigation
-
Testing Strategy
- Unit tests for core logic (vitest or jest)
- Integration tests for MCP interactions
- End-to-end tests simulating real user journeys
- Load testing to understand resource constraints
- Chaos testing: simulate network failures, timeouts, downstream service outages
Remember: The goal isn't just to deploy something that works — it's to deploy something that continues to work reliably as usage grows and conditions change.