branch:
getting-started.md
7379 bytesRaw
# Getting Started

Build AI agents that persist, think, and act. Agents run on Cloudflare's global network, maintain state across requests, and connect to clients in real-time via WebSockets.

**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

```bash
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

Start the dev server:

```bash
npm run dev
```

Open [http://localhost:5173](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`:

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

Update `wrangler.jsonc` to register the agent:

```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"]
    }
  ]
}
```

---

## Connect from React

Replace `src/client.tsx`:

```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 points:

- **`useAgent`** connects to your agent via WebSocket
- **`onStateUpdate`** fires whenever the agent's state changes
- **`agent.stub.methodName()`** calls methods marked with `@callable()` on your agent

---

## What Just Happened?

When you clicked the button:

1. **Client** called `agent.stub.increment()` over WebSocket
2. **Agent** ran `increment()`, updated state with `setState()`
3. **State** persisted to SQLite automatically
4. **Broadcast** sent to all connected clients
5. **React** updated via `onStateUpdate`

```
┌─────────────┐         ┌─────────────┐
│   Browser   │◄───────►│    Agent    │
│  (React)    │   WS    │  (Counter)  │
└─────────────┘         └──────┬──────┘
                               │
                        ┌──────▼──────┐
                        │   SQLite    │
                        │  (State)    │
                        └─────────────┘
```

### Key Concepts

| Concept              | What it means                                                                               |
| -------------------- | ------------------------------------------------------------------------------------------- |
| **Agent instance**   | Each unique name gets its own agent. `Counter:user-123` is separate from `Counter:user-456` |
| **Persistent state** | State survives restarts, deploys, and hibernation. It's stored in SQLite                    |
| **Real-time sync**   | All clients connected to the same agent receive state updates instantly                     |
| **Hibernation**      | 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:

```typescript
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

```bash
npm run deploy
```

Your agent is now live on Cloudflare's global network, running close to your users.

---

## Next Steps

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

- **[State Management](./state.md)** - Deep dive into `setState()`, `initialState`, and `onStateChanged()`
- **[Client SDK](./client-sdk.md)** - Full `useAgent` and `AgentClient` API reference
- **[Scheduling](./scheduling.md)** - Run tasks on a delay, schedule, or cron
- **[Agent Class](./agent-class.md)** - Lifecycle methods, HTTP handlers, and WebSocket events

### Common Patterns

| I want to...             | Read...                                  |
| ------------------------ | ---------------------------------------- |
| Add AI/LLM capabilities  | [Chat Agents](./chat-agents.md)          |
| Expose tools via MCP     | [Creating MCP Servers](./mcp-servers.md) |
| Run background tasks     | [Scheduling](./scheduling.md)            |
| Handle emails            | [Email Routing](./email.md)              |
| Use Cloudflare Workflows | [Workflows](./workflows.md)              |

---

## Troubleshooting

### "Agent not found" / 404 errors

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)

### State not syncing

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)

### "Method X is not callable" errors

Make sure your methods are decorated with `@callable()`:

```typescript
import { callable } from "agents";

@callable()
increment() {
  // ...
}
```

### Type errors with `agent.stub`

Add the agent type parameter:

```typescript
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
```