Skip to main content

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

1

Install the package

npm install agents
For React applications, no additional packages are needed—React bindings are included.
2

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):
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:
wrangler.jsonc
{
  "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:
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:
For projects using the standard fetch handler:
src/index.ts
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 });
  }
};

Add TypeScript Types

Update your Env type to include the agent namespace. Create or update env.d.ts:
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>
  );
}

Adding Multiple Agents

Add more agents by extending the configuration:
src/agents/chat.ts
export class Chat extends Agent<Env, ChatState> {
  // ...
}
src/agents/scheduler.ts
export class Scheduler extends Agent<Env> {
  // ...
}
Update wrangler.jsonc:
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:
src/index.ts
export { Counter } from "./agents/counter";
export { Chat } from "./agents/chat";
export { Scheduler } from "./agents/scheduler";

Common Integration Patterns

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}
});
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

  1. Check the export - Agent class must be exported from your main entry point
  2. Check the binding - class_name in wrangler.jsonc must match the exported class name exactly
  3. Check the route - Default route is /agents/{agent-name}/{instance-name}
Add the migration to wrangler.jsonc:
"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["YourAgentClass"]
  }
]
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:
  1. You’re using this.setState(), not mutating this.state directly
  2. The agent class is in new_sqlite_classes in migrations
  3. 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

Build docs developers (and LLMs) love