| name | agents-sdk |
| description | Build AI agents on Cloudflare Workers using the Agents SDK or build Model Context Protocol (MCP) servers. Load when creating stateful agents, durable workflows, MCP servers, checking MCP schema/error responses, or reviewing MCP code quality. Covers Agent class, state management, callable RPC, Workflows integration, and MCP implementation rules. Do NOT trigger for generic 'build a server' requests unless the platform is explicitly specified as Cloudflare Agents or an MCP server. |
Cloudflare Agents SDK
STOP. Your knowledge of the Agents SDK may be outdated. Prefer retrieval over pre-training for any Agents SDK task.
Documentation
Fetch current docs from https://github.com/cloudflare/agents/tree/main/docs before implementing.
| Topic | Doc | Use for |
|---|
| Getting started | docs/getting-started.md | First agent, project setup |
| State | docs/state.md | setState, validateStateChange, persistence |
| Routing | docs/routing.md | URL patterns, routeAgentRequest, basePath |
| Callable methods | docs/callable-methods.md | @callable, RPC, streaming, timeouts |
| Scheduling | docs/scheduling.md | schedule(), scheduleEvery(), cron |
| Workflows | docs/workflows.md | AgentWorkflow, durable multi-step tasks |
| HTTP/WebSockets | docs/http-websockets.md | Lifecycle hooks, hibernation |
| Email | docs/email.md | Email routing, secure reply resolver |
| MCP client | docs/mcp-client.md | Connecting to MCP servers |
| MCP server | docs/mcp-servers.md | Building MCP servers with McpAgent |
| Client SDK | docs/client-sdk.md | useAgent, useAgentChat, React hooks |
| Human-in-the-loop | docs/human-in-the-loop.md | Approval flows, pausing workflows |
| Resumable streaming | docs/resumable-streaming.md | Stream recovery on disconnect |
Cloudflare docs: https://developers.cloudflare.com/agents/
Capabilities
The Agents SDK provides:
- Persistent state - SQLite-backed, auto-synced to clients
- Callable RPC -
@callable() methods invoked over WebSocket
- Scheduling - One-time, recurring (
scheduleEvery), and cron tasks
- Workflows - Durable multi-step background processing via
AgentWorkflow
- MCP integration - Connect to MCP servers or build your own with
McpAgent
- Email handling - Receive and reply to emails with secure routing
- Streaming chat -
AIChatAgent with resumable streams
- React hooks -
useAgent, useAgentChat for client apps
Quick Start (Project Initialization)
When asked to scaffold a new agent, use the official Cloudflare CLI template:
npm create cloudflare@latest -- my-agent --template=cloudflare/agents-starter
cd my-agent
npm start
Once implemented, deploy using:
npx wrangler deploy
wrangler tail
FIRST: Verify Installation
npm ls agents
If not installed:
npm install agents
Wrangler Configuration
{
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }],
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }],
}
Agent Class
import { Agent, routeAgentRequest, callable } from 'agents'
type State = { count: number }
export class Counter extends Agent<Env, State> {
initialState = { count: 0 }
validateStateChange(nextState: State, source: Connection | 'server') {
if (nextState.count < 0) throw new Error('Count cannot be negative')
}
onStateUpdate(state: State, source: Connection | 'server') {
console.log('State updated:', state)
}
@callable()
increment() {
this.setState({ count: this.state.count + 1 })
return this.state.count
}
}
export default {
fetch: (req, env) => routeAgentRequest(req, env) ?? new Response('Not found', { status: 404 }),
}
Routing
Requests route to /agents/{agent-name}/{instance-name}:
| Class | URL |
|---|
Counter | /agents/counter/user-123 |
ChatRoom | /agents/chat-room/lobby |
Client: useAgent({ agent: "Counter", name: "user-123" })
Core APIs
| Task | API |
|---|
| Read state | this.state.count |
| Write state | this.setState({ count: 1 }) |
| SQL query | this.sql`SELECT * FROM users WHERE id = ${id}` |
| Schedule (delay) | await this.schedule(60, "task", payload) |
| Schedule (cron) | await this.schedule("0 * * * *", "task", payload) |
| Schedule (interval) | await this.scheduleEvery(30, "poll") |
| RPC method | @callable() myMethod() { ... } |
| Streaming RPC | @callable({ streaming: true }) stream(res) { ... } |
| Start workflow | await this.runWorkflow("ProcessingWorkflow", params) |
React Client
import { useAgent } from 'agents/react'
function App() {
const [state, setLocalState] = useState({ count: 0 })
const agent = useAgent({
agent: 'Counter',
name: 'my-instance',
onStateUpdate: (newState) => setLocalState(newState),
onIdentity: (name, agentType) => console.log(`Connected to ${name}`),
})
return (
<button onClick={() => agent.setState({ count: state.count + 1 })}>Count: {state.count}</button>
)
}
References
Security
- Input Validation: Validate all WebSocket messages with Zod before processing. Catch
JSON.parse errors explicitly.
- RPC Security: Define explicit input schemas (e.g. Zod) for all
@callable() methods to prevent malformed execution.
MCP Server Construction (Core Rules)
When building or modifying MCP servers, follow these prioritized rules:
- Must: Use Zod for all tool argument validation. Do not blindly trust MCP client inputs.
- Must: Return structured errors (
{ isError: true, content: [...] }) from tools rather than throwing raw unhandled exceptions that crash the server.
- Should: Wrap handlers in try/catch blocks that gracefully surface errors to the LLM context.
- Should: Centralize error logging using standard prefixes (e.g.,
[MCP Error]).
- Optional: Depending on the repository, implement integration testing via Playwright for dual HTTP/SSE verification.
Security (Defense-in-Depth):
- Blocklists are Defense-in-Depth: A blocklist is not a primary security boundary. Primary security is the sandbox, container, or strict schema validation.
- No Secrets in Config: MCP servers must rely on the environment variables for API keys and secrets, never hardcoded files inside the server repository.
- Rate Limiting & Input Sanitization: Aggressively sanitize path arguments to prevent directory traversal, and apply basic rate-limiting.
MCP Scaffold Reference:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
const server = new McpServer({ name: 'my-mcp', version: '1.0.0' })
server.tool(
'my_tool',
'Does something using an ID',
{ id: z.string().describe('The user ID to process') },
async ({ id }) => {
try {
return { content: [{ type: 'text', text: `Got ID: ${id}` }] }
} catch (err) {
return { isError: true, content: [{ type: 'text', text: `Error: ${err}` }] }
}
}
)
const transport = new StdioServerTransport()
await server.connect(transport)
For advanced MCP references, consult references/mcp/ (Code Mode, OAuth, Implementation Guides).