> ## 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.

# Adding to Existing Project

> Integrate agents into your existing Cloudflare Workers application

This guide shows how to add agents to an existing Cloudflare Workers project. If you're starting fresh, see [Quick Start](/quickstart) instead.

## Prerequisites

<CardGroup cols={2}>
  <Card title="Cloudflare Workers Project" icon="cloudflare">
    An existing project with `wrangler.jsonc`
  </Card>

  <Card title="Node.js 18+" icon="node-js">
    Required for the agents SDK
  </Card>
</CardGroup>

## Installation

<Steps>
  <Step title="Install the package">
    ```bash theme={null}
    npm install agents
    ```

    For React applications, no additional packages are needed—React bindings are included.
  </Step>

  <Step title="For Hono applications">
    If you're using Hono, install the Hono integration:

    ```bash theme={null}
    npm install agents hono-agents
    ```
  </Step>
</Steps>

## Create an Agent

Create a new file for your agent (e.g., `src/agents/counter.ts`):

```typescript src/agents/counter.ts theme={null}
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:

```jsonc wrangler.jsonc theme={null}
{
  "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"]
    }
  ]
}
```

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

## Export the Agent Class

Your agent class must be exported from your main entry point. Update your `src/index.ts`:

```typescript src/index.ts theme={null}
// 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:

<Tabs>
  <Tab title="Plain Workers">
    For projects using the standard `fetch` handler:

    ```typescript src/index.ts theme={null}
    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 });
      }
    };
    ```
  </Tab>

  <Tab title="Hono">
    For projects using the Hono framework:

    ```typescript src/index.ts theme={null}
    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;
    ```
  </Tab>

  <Tab title="With Static Assets">
    If you're serving static assets alongside agents:

    ```typescript src/index.ts theme={null}
    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:

    ```jsonc theme={null}
    {
      "assets": {
        "binding": "ASSETS"
      }
    }
    ```
  </Tab>
</Tabs>

## Add TypeScript Types

Update your `Env` type to include the agent namespace. Create or update `env.d.ts`:

```typescript env.d.ts theme={null}
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

<Tabs>
  <Tab title="React">
    ```tsx src/components/CounterWidget.tsx theme={null}
    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>
      );
    }
    ```
  </Tab>

  <Tab title="Vanilla JavaScript">
    ```typescript src/client.ts theme={null}
    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");
    ```
  </Tab>
</Tabs>

## Adding Multiple Agents

Add more agents by extending the configuration:

```typescript src/agents/chat.ts theme={null}
export class Chat extends Agent<Env, ChatState> {
  // ...
}
```

```typescript src/agents/scheduler.ts theme={null}
export class Scheduler extends Agent<Env> {
  // ...
}
```

Update `wrangler.jsonc`:

```jsonc wrangler.jsonc theme={null}
{
  "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:

```typescript src/index.ts theme={null}
export { Counter } from "./agents/counter";
export { Chat } from "./agents/chat";
export { Scheduler } from "./agents/scheduler";
```

## Common Integration Patterns

<AccordionGroup>
  <Accordion title="Agents Behind Authentication" icon="lock">
    Check auth before routing to agents:

    ```typescript theme={null}
    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
      }
    };
    ```
  </Accordion>

  <Accordion title="Custom Agent Path Prefix" icon="route">
    By default, agents are routed at `/agents/{agent-name}/{instance-name}`. You can customize this:

    ```typescript theme={null}
    import { routeAgentRequest } from "agents";

    const agentResponse = await routeAgentRequest(request, env, {
      prefix: "/api/agents" // Now routes at /api/agents/{agent-name}/{instance-name}
    });
    ```
  </Accordion>

  <Accordion title="Accessing Agents from Server Code" icon="server">
    You can interact with agents directly from your Worker code:

    ```typescript theme={null}
    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 });
        }
        // ...
      }
    };
    ```
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Agent not found or 404 errors" icon="triangle-exclamation">
    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}`
  </Accordion>

  <Accordion title="No such Durable Object class error" icon="triangle-exclamation">
    Add the migration to `wrangler.jsonc`:

    ```jsonc theme={null}
    "migrations": [
      {
        "tag": "v1",
        "new_sqlite_classes": ["YourAgentClass"]
      }
    ]
    ```
  </Accordion>

  <Accordion title="WebSocket connection fails" icon="triangle-exclamation">
    Ensure your routing passes the response through unchanged:

    ```typescript theme={null}
    // ✅ 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
    ```
  </Accordion>

  <Accordion title="State not persisting" icon="triangle-exclamation">
    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
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="State Management" icon="database" href="/core/state-management">
    Deep dive into agent state
  </Card>

  <Card title="Scheduling" icon="clock" href="/background/scheduling">
    Background tasks and cron jobs
  </Card>

  <Card title="Agent Class" icon="cube" href="/core/agent-class">
    Full lifecycle and methods
  </Card>

  <Card title="Client SDK" icon="code" href="/client/client-sdk">
    Complete client API reference
  </Card>
</CardGroup>
