Documentation Index Fetch the complete documentation index at: https://mintlify.com/cloudflare/agents/llms.txt
Use this file to discover all available pages before exploring further.
This guide shows how to add agents to an existing Cloudflare Workers project. If you’re starting fresh, see Quick Start instead.
Prerequisites
Cloudflare Workers Project An existing project with wrangler.jsonc
Node.js 18+ Required for the agents SDK
Installation
Install the package
For React applications, no additional packages are needed—React bindings are included.
For Hono applications
If you’re using Hono, install the Hono integration: npm install agents hono-agents
Create an Agent
Create a new file for your agent (e.g., src/agents/counter.ts):
import { Agent , callable } from "agents" ;
type CounterState = {
count : number ;
};
export class Counter extends Agent < Env , CounterState > {
initialState : CounterState = { count: 0 };
@ callable ()
increment () {
this . setState ({ count: this . state . count + 1 });
return this . state . count ;
}
@ callable ()
decrement () {
this . setState ({ count: this . state . count - 1 });
return this . state . count ;
}
}
Update wrangler.jsonc
Add the Durable Object binding and migration:
{
"name" : "my-existing-project" ,
"main" : "src/index.ts" ,
"compatibility_date" : "2025-01-01" ,
"compatibility_flags" : [ "nodejs_compat" ], // Required for agents
// Add this section
"durable_objects" : {
"bindings" : [
{
"name" : "Counter" ,
"class_name" : "Counter"
}
]
},
// Add this section
"migrations" : [
{
"tag" : "v1" ,
"new_sqlite_classes" : [ "Counter" ]
}
]
}
Key points:
name in bindings becomes the property on env (e.g., env.Counter)
class_name must match your exported class name exactly
new_sqlite_classes enables SQLite storage for state persistence
The nodejs_compat flag is required for the agents package
Export the Agent Class
Your agent class must be exported from your main entry point. Update your src/index.ts:
// Export the agent class (required for Durable Objects)
export { Counter } from "./agents/counter" ;
// Your existing exports...
export default {
// ...
} ;
Wire Up Routing
Choose the approach that matches your project structure:
Plain Workers
Hono
With Static Assets
For projects using the standard fetch handler: import { routeAgentRequest } from "agents" ;
export { Counter } from "./agents/counter" ;
export default {
async fetch ( request : Request , env : Env , ctx : ExecutionContext ) {
// Try agent routing first
const agentResponse = await routeAgentRequest ( request , env );
if ( agentResponse ) return agentResponse ;
// Your existing routing logic
const url = new URL ( request . url );
if ( url . pathname === "/api/hello" ) {
return Response . json ({ message: "Hello!" });
}
return new Response ( "Not found" , { status: 404 });
}
} ;
For projects using the Hono framework: import { Hono } from "hono" ;
import { agentsMiddleware } from "hono-agents" ;
export { Counter } from "./agents/counter" ;
const app = new Hono <{ Bindings : Env }>();
// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
app . use ( "*" , agentsMiddleware ());
// Your existing routes continue to work
app . get ( "/api/hello" , ( c ) => c . json ({ message: "Hello!" }));
export default app ;
If you’re serving static assets alongside agents: import { routeAgentRequest } from "agents" ;
export { Counter } from "./agents/counter" ;
export default {
async fetch ( request : Request , env : Env , ctx : ExecutionContext ) {
// Try agent routing first
const agentResponse = await routeAgentRequest ( request , env );
if ( agentResponse ) return agentResponse ;
// Fall back to static assets
return env . ASSETS . fetch ( request );
}
} ;
Make sure your wrangler.jsonc has the assets binding: {
"assets" : {
"binding" : "ASSETS"
}
}
Add TypeScript Types
Update your Env type to include the agent namespace. Create or update env.d.ts:
import type { Counter } from "./agents/counter" ;
interface Env {
// Your existing bindings
MY_KV : KVNamespace ;
MY_DB : D1Database ;
// Add agent bindings
Counter : DurableObjectNamespace < Counter >;
}
Connect from the Frontend
src/components/CounterWidget.tsx
import { useState } from "react" ;
import { useAgent } from "agents/react" ;
type CounterState = { count : number };
function CounterWidget () {
const [ count , setCount ] = useState ( 0 );
const agent = useAgent < CounterState >({
agent: "Counter" ,
onStateUpdate : ( state ) => setCount ( state . count )
});
return (
< div >
< span > { count } </ span >
< button onClick = { () => agent . stub . increment () } > + </ button >
< button onClick = { () => agent . stub . decrement () } > - </ button >
</ div >
);
}
import { AgentClient } from "agents/client" ;
const agent = new AgentClient ({
agent: "Counter" ,
name: "user-123" , // Optional: unique instance name
onStateUpdate : ( state ) => {
document . getElementById ( "count" ). textContent = state . count ;
}
});
// Call methods
document . getElementById ( "increment" ). onclick = () => agent . call ( "increment" );
Adding Multiple Agents
Add more agents by extending the configuration:
export class Chat extends Agent < Env , ChatState > {
// ...
}
export class Scheduler extends Agent < Env > {
// ...
}
Update wrangler.jsonc:
{
"durable_objects" : {
"bindings" : [
{ "name" : "Counter" , "class_name" : "Counter" },
{ "name" : "Chat" , "class_name" : "Chat" },
{ "name" : "Scheduler" , "class_name" : "Scheduler" }
]
},
"migrations" : [
{
"tag" : "v1" ,
"new_sqlite_classes" : [ "Counter" , "Chat" , "Scheduler" ]
}
]
}
Export all agents from your entry point:
export { Counter } from "./agents/counter" ;
export { Chat } from "./agents/chat" ;
export { Scheduler } from "./agents/scheduler" ;
Common Integration Patterns
Agents Behind Authentication
Check auth before routing to agents: export default {
async fetch ( request : Request , env : Env ) {
// Check auth for agent routes
if ( request . url . includes ( "/agents/" )) {
const authResult = await checkAuth ( request , env );
if ( ! authResult . valid ) {
return new Response ( "Unauthorized" , { status: 401 });
}
}
const agentResponse = await routeAgentRequest ( request , env );
if ( agentResponse ) return agentResponse ;
// ... rest of routing
}
} ;
By default, agents are routed at /agents/{agent-name}/{instance-name}. You can customize this: import { routeAgentRequest } from "agents" ;
const agentResponse = await routeAgentRequest ( request , env , {
prefix: "/api/agents" // Now routes at /api/agents/{agent-name}/{instance-name}
});
Accessing Agents from Server Code
You can interact with agents directly from your Worker code: import { getAgentByName } from "agents" ;
export default {
async fetch ( request : Request , env : Env ) {
if ( request . url . endsWith ( "/api/increment" )) {
// Get a specific agent instance
const counter = await getAgentByName ( env . Counter , "shared-counter" );
const newCount = await counter . increment ();
return Response . json ({ count: newCount });
}
// ...
}
} ;
Troubleshooting
Agent not found or 404 errors
Check the export - Agent class must be exported from your main entry point
Check the binding - class_name in wrangler.jsonc must match the exported class name exactly
Check the route - Default route is /agents/{agent-name}/{instance-name}
No such Durable Object class error
Add the migration to wrangler.jsonc: "migrations" : [
{
"tag" : "v1" ,
"new_sqlite_classes" : [ "YourAgentClass" ]
}
]
WebSocket connection fails
Ensure your routing passes the response through unchanged: // ✅ Correct - return the response directly
const agentResponse = await routeAgentRequest ( request , env );
if ( agentResponse ) return agentResponse ;
// ❌ Wrong - don't wrap or modify the response
const agentResponse = await routeAgentRequest ( request , env );
if ( agentResponse ) return new Response ( agentResponse . body ); // Breaks WebSocket
Check that:
You’re using this.setState(), not mutating this.state directly
The agent class is in new_sqlite_classes in migrations
You’re connecting to the same agent instance name
Next Steps
State Management Deep dive into agent state
Scheduling Background tasks and cron jobs
Agent Class Full lifecycle and methods
Client SDK Complete client API reference