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.

What you’ll build: A counter agent with persistent state that syncs to a React frontend in real-time.Time: ~10 minutes

Create a New Project

Use the Cloudflare agents starter template to scaffold a new project:
npm create cloudflare@latest -- --template cloudflare/agents-starter
cd my-agent
npm install
This creates a project with:
  • src/server.ts - Your agent code
  • src/client.tsx - React frontend
  • wrangler.jsonc - Cloudflare configuration
1

Start the dev server

npm run dev
Open http://localhost:5173 to see your agent in action.

Your First Agent

Let’s build a simple counter agent from scratch. Replace src/server.ts:
src/server.ts
import { Agent, routeAgentRequest, callable } from "agents";

// Define the state shape
type CounterState = {
  count: number;
};

// Create the agent
export class Counter extends Agent<Env, CounterState> {
  // Initial state for new instances
  initialState: CounterState = { count: 0 };

  // Methods marked with @callable can be called from the client
  @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;
  }

  @callable()
  reset() {
    this.setState({ count: 0 });
  }
}

// Route requests to agents
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return (
      (await routeAgentRequest(request, env)) ??
      new Response("Not found", { status: 404 })
    );
  }
};
Methods must be decorated with @callable() to be invoked from clients.

Configure the Agent

Update wrangler.jsonc to register the agent:
wrangler.jsonc
{
  "name": "my-agent",
  "main": "src/server.ts",
  "compatibility_date": "2025-01-01",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [
      {
        "name": "Counter",
        "class_name": "Counter"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["Counter"]
    }
  ]
}
  • 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

Connect from React

Replace src/client.tsx to connect to your agent:
src/client.tsx
import { useState } from "react";
import { useAgent } from "agents/react";

// Match your agent's state type
type CounterState = {
  count: number;
};

export default function App() {
  const [count, setCount] = useState(0);

  // Connect to the Counter agent
  const agent = useAgent<CounterState>({
    agent: "Counter",
    onStateUpdate: (state) => setCount(state.count)
  });

  return (
    <div style={{ padding: "2rem", fontFamily: "system-ui" }}>
      <h1>Counter Agent</h1>
      <p style={{ fontSize: "3rem" }}>{count}</p>
      <div style={{ display: "flex", gap: "1rem" }}>
        <button onClick={() => agent.stub.decrement()}>-</button>
        <button onClick={() => agent.stub.reset()}>Reset</button>
        <button onClick={() => agent.stub.increment()}>+</button>
      </div>
    </div>
  );
}

Key Concepts

useAgent

Connects to your agent via WebSocket

onStateUpdate

Fires whenever the agent’s state changes

agent.stub

Calls methods marked with @callable() on your agent

What Just Happened?

When you clicked the button:
1

Client called agent.stub.increment()

The call is sent over WebSocket to the agent
2

Agent ran increment()

Updated state with setState()
3

State persisted to SQLite

Happens automatically on every setState() call
4

Broadcast sent to all clients

All connected clients receive the state update
5

React updated via onStateUpdate

Your UI re-renders with the new state

Understanding Agent Instances

Each unique name gets its own agent. Counter:user-123 is separate from Counter:user-456
State survives restarts, deploys, and hibernation. It’s stored in SQLite
All clients connected to the same agent receive state updates instantly
When no clients are connected, the agent hibernates (no cost). It wakes on the next request

Connect from Vanilla JS

If you’re not using React:
import { AgentClient } from "agents/client";

const agent = new AgentClient({
  agent: "Counter",
  name: "my-counter", // optional, defaults to "default"
  onStateUpdate: (state) => {
    console.log("New count:", state.count);
  }
});

// Call methods
await agent.call("increment");
await agent.call("reset");

Deploy to Cloudflare

When you’re ready to deploy:
npm run deploy
Your agent is now live on Cloudflare’s global network, running close to your users.

Troubleshooting

Make sure:
  1. Agent class is exported from your server file
  2. wrangler.jsonc has the binding and migration
  3. Agent name in client matches the class name (case-insensitive)
Check that:
  1. You’re calling this.setState(), not mutating this.state directly
  2. The onStateUpdate callback is wired up in your client
  3. WebSocket connection is established (check browser dev tools)
Make sure your methods are decorated with @callable():
import { callable } from "agents";

@callable()
increment() {
  // ...
}
Add the agent type parameter:
const agent = useAgent<Counter, CounterState>({
  agent: "Counter",
  onStateUpdate: (state) => setCount(state.count)
});

// Now agent.stub is fully typed
agent.stub.increment(); // ✓ TypeScript knows this method exists

Next Steps

Now that you have a working agent, explore these topics:

State Management

Deep dive into setState(), initialState, and onStateChanged()

Client SDK

Full useAgent and AgentClient API reference

Scheduling

Run tasks on a delay, schedule, or cron

Agent Class

Lifecycle methods, HTTP handlers, and WebSocket events

Common Use Cases

I want to…Read…
Add AI/LLM capabilitiesChat Agents
Expose tools via MCPCreating MCP Servers
Run background tasksScheduling
Handle emailsEmail Routing
Use Cloudflare WorkflowsWorkflows

Build docs developers (and LLMs) love