Configuration (5)

Last updated Oct 12, 2026 This guide covers everything you need to configure agents for local development and production deployment, including Wrangler configuration file setup, type generation, environment variables, and the Cloudflare dashboard.

Project structure

The typical file structure for an Agent project created from npm create cloudflare@latest agents-starter -- --template cloudflare/agents-starter follows:

  • src/
  • index.ts your Agent definition
  • public/
  • index.html
  • test/
  • index.spec.ts your tests
  • package.json
  • tsconfig.json
  • vitest.config.mts
  • worker-configuration.d.ts
  • wrangler.jsonc your Workers and Agent configuration

Wrangler configuration file

The wrangler.jsonc file configures your Cloudflare Worker and its bindings. Here is a complete example for an agents project:

{
    "$schema": "node_modules/wrangler/config-schema.json",
    "name": "my-agent-app",
    "main": "src/server.ts",
    "upload_source_maps": true,
    // Set this to today's date
    "compatibility_date": "2026-10-27",
    "compatibility_flags": ["nodejs_compat"],

// Static assets (optional)
    "assets": {
        "directory": "public",
        "binding": "ASSETS",
    },

// Durable Object bindings for agents
    "durable_objects": {
        "bindings": [
            {
                "name": "MyAgent",
                "class_name": "MyAgent",
            },
            {
                "name": "ChatAgent",
                "class_name": "ChatAgent",
            },
        ],
    },

// Provision storage for each agent class
    "exports": {
        "MyAgent": {
            "type": "durable-object",
            "storage": "sqlite",
        },
        "ChatAgent": {
            "type": "durable-object",
            "storage": "sqlite",
        },
    },

// AI binding (optional, for Workers AI)
    "ai": {
        "binding": "AI",
    },

// Observability (recommended)
    "observability": {
        "enabled": true,
    },
}
"$schema" = "node_modules/wrangler/config-schema.json"
name = "my-agent-app"
main = "src/server.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-27"
compatibility_flags = [ "nodejs_compat" ]

[assets]
directory = "public"
binding = "ASSETS"

[[durable_objects.bindings]]
name = "MyAgent"
class_name = "MyAgent"

[[durable_objects.bindings]]
name = "ChatAgent"
class_name = "ChatAgent"

[exports.MyAgent]
type = "durable-object"
storage = "sqlite"

[exports.ChatAgent]
type = "durable-object"
storage = "sqlite"

[ai]
binding = "AI"

[observability]
enabled = true

Key fields

compatibility_flags

The nodejs_compat flag is required for agents:

{
    "compatibility_flags": ["nodejs_compat"],
}
compatibility_flags = [ "nodejs_compat" ]

This enables Node.js compatibility mode, which agents depend on for crypto, streams, and other Node.js APIs.

durable_objects.bindings

Each agent class needs a binding:

{
    "durable_objects": {
        "bindings": [
            {
                "name": "Counter",
                "class_name": "Counter",
            },
        ],
    },
}
[[durable_objects.bindings]]
name = "Counter"
class_name = "Counter"
Field Description
name The property name on env. Use this in code: env.Counter
class_name Must match the exported class name exactly

When `name` and `class_name` differ

When name and class_name differ, follow the pattern shown below:

{
    "durable_objects": {
        "bindings": [
            {
                "name": "COUNTER_DO",
                "class_name": "CounterAgent",
            },
        ],
    },
}
[[durable_objects.bindings]]
name = "COUNTER_DO"
class_name = "CounterAgent"

This is useful when you want environment variable-style naming (COUNTER_DO) but more descriptive class names (CounterAgent).

exports

The exports field declares each Agent class your Worker exports and the storage backend Cloudflare should use for it:

{
    "exports": {
        "MyAgent": {
            "type": "durable-object",
            "storage": "sqlite",
        },
    },
}
[exports.MyAgent]
type = "durable-object"
storage = "sqlite"
Field Description
type The kind of export. Always "durable-object" for an Agent.
storage The storage backend. Use "sqlite" for new Agents (recommended).

For details on renaming, deleting, or transferring Agent classes, refer to Durable Object class exports. Existing Workers using the legacy migrations array continue to work — refer to Durable Object class migrations (legacy).

assets

For serving static files (HTML, CSS, JS):

{
    "assets": {
        "directory": "public",
        "binding": "ASSETS",
    },
}
[assets]
directory = "public"
binding = "ASSETS"

With a binding, you can serve assets programmatically:

export default {
    async fetch(request, env) {
        // Static assets are served by the worker automatically by default

// Route the request to the appropriate agent
        const agentResponse = await routeAgentRequest(request, env);
        if (agentResponse) return agentResponse;

// Add your own routing logic here
        return new Response("Not found", { status: 404 });
    },
};
export default {
    async fetch(request: Request, env: Env) {
        // Static assets are served by the worker automatically by default

// Route the request to the appropriate agent
        const agentResponse = await routeAgentRequest(request, env);
        if (agentResponse) return agentResponse;

// Add your own routing logic here
        return new Response("Not found", { status: 404 });
    },
} satisfies ExportedHandler<Env>;

ai

For Workers AI integration:

{
    "ai": {
        "binding": "AI",
    },
}
[ai]
binding = "AI"

Access in your agent:

const response = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {
    prompt: "Hello!",
});
const response = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {
    prompt: "Hello!",
});

TypeScript configuration

The Agents SDK ships a shared tsconfig.json that sets all the compiler options needed for agents projects — including the ES2021 target required for @callable() decorators, strict mode, bundler module resolution, and Workers types.

Extend it in your tsconfig.json:

{
    "extends": "agents/tsconfig"
}

This is equivalent to:

{
    "compilerOptions": {
        "target": "ES2021",
        "lib": ["ES2022", "DOM", "DOM.Iterable"],
        "jsx": "react-jsx",
        "module": "ES2022",
        "moduleResolution": "bundler",
        "types": ["node", "@cloudflare/workers-types", "vite/client"],
        "allowImportingTsExtensions": true,
        "noEmit": true,
        "isolatedModules": true,
        "verbatimModuleSyntax": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    }
}

You can override individual options as needed:

{
    "extends": "agents/tsconfig",
    "compilerOptions": {
        "jsx": "preserve"
    }
}

Caution

Do not set "experimentalDecorators": true. The Agents SDK uses TC39 standard decorators ↗︎, not TypeScript legacy decorators. Enabling experimentalDecorators applies an incompatible transform that silently breaks @callable() at runtime.

Vite configuration

The Agents SDK provides a Vite plugin that handles TC39 decorator transforms. Vite 8 uses Oxc for transpilation, which does not yet support TC39 decorators — without this plugin, @callable() and other decorators will fail at runtime.

Add the plugin to your vite.config.ts:

import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
import agents from "agents/vite";
import { defineConfig } from "vite";

export default defineConfig({
    plugins: [agents(), react(), cloudflare()],
});

vite.config.tsts

import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
import agents from "agents/vite";
import { defineConfig } from "vite";

export default defineConfig({
    plugins: [agents(), react(), cloudflare()],
});

The agents() plugin is safe to include even if your project does not use decorators. It only runs the transform on files that contain @ syntax.

The starter template and all examples include this plugin by default. If you encounter SyntaxError: Invalid or unexpected token with decorators, refer to Callable methods — Troubleshooting.

Generating types

Wrangler can generate TypeScript types for your bindings.

Automatic generation

Run the types command:

npx wrangler types

This creates or updates worker-configuration.d.ts with your Env type.

Custom output path

Specify a custom path:

npx wrangler types env.d.ts

Without runtime types

For cleaner output (recommended for agents):

npx wrangler types env.d.ts --include-runtime false

This generates just your bindings without Cloudflare runtime types.

Example generated output

// env.d.ts (generated)
declare namespace Cloudflare {
    interface Env {
        OPENAI_API_KEY: string;
        Counter: DurableObjectNamespace;
        ChatAgent: DurableObjectNamespace;
    }
}
interface Env extends Cloudflare.Env {}

Manual type definition

You can also define types manually:

// env.d.ts
// env.d.ts
import type { Counter } from "./src/agents/counter";
import type { ChatAgent } from "./src/agents/chat";

interface Env {
    // Secrets
    OPENAI_API_KEY: string;
    WEBHOOK_SECRET: string;

// Agent bindings
    Counter: DurableObjectNamespace<Counter>;
    ChatAgent: DurableObjectNamespace<ChatAgent>;

// Other bindings
    AI: Ai;
    ASSETS: Fetcher;
    MY_KV: KVNamespace;
}

Adding to package.json

Add a script for easy regeneration:

{
    "scripts": {
        "types": "wrangler types env.d.ts --include-runtime false"
    }
}

Environment variables and secrets

Local development (.env)

Create a .env file for local secrets (add to .gitignore):

# .env
OPENAI_API_KEY=sk-...
GITHUB_WEBHOOK_SECRET=whsec_...
DATABASE_URL=postgres://...

Access in your agent:

class MyAgent extends Agent {
    async onStart() {
        const apiKey = this.env.OPENAI_API_KEY;
    }
}
class MyAgent extends Agent {
    async onStart() {
        const apiKey = this.env.OPENAI_API_KEY;
    }
}

Production secrets

Use wrangler secret for production:

# Add a secret
npx wrangler secret put OPENAI_API_KEY
# Enter value when prompted

# List secrets
npx wrangler secret list

# Delete a secret
npx wrangler secret delete OPENAI_API_KEY

Non-secret variables

For non-sensitive configuration, use vars in the Wrangler configuration file:

{
    "vars": {
        "API_BASE_URL": "https://api.example.com",
        "MAX_RETRIES": "3",
        "DEBUG_MODE": "false",
    },
}
[vars]
API_BASE_URL = "https://api.example.com"
MAX_RETRIES = "3"
DEBUG_MODE = "false"

All values must be strings. Parse numbers and booleans in code:

const maxRetries = parseInt(this.env.MAX_RETRIES, 10);
const debugMode = this.env.DEBUG_MODE === "true";
const maxRetries = parseInt(this.env.MAX_RETRIES, 10);
const debugMode = this.env.DEBUG_MODE === "true";

Environment-specific variables

Use env sections for different environments (for example, staging, production):

{
    "name": "my-agent",
    "vars": {
        "API_URL": "https://api.example.com",
    },

"env": {
        "staging": {
            "vars": {
                "API_URL": "https://staging-api.example.com",
            },
        },
        "production": {
            "vars": {
                "API_URL": "https://api.example.com",
            },
        },
    },
}
name = "my-agent"

[vars]
API_URL = "https://api.example.com"

[env.staging.vars]
API_URL = "https://staging-api.example.com"

[env.production.vars]
API_URL = "https://api.example.com"

Deploy to specific environment:

npx wrangler deploy --env staging
npx wrangler deploy --env production