# Building AI agents on Cloudflare: which architecture when

> Project Think, Agents SDK, AI SDK, Workflows, MCP or a plain Worker? A decision tree, layer diagram and ten architectures with starter code.

By Kai Bitterschulte (https://kai.zone/). Published 2026-10-07, updated 2026-10-08. Canonical URL: https://kai.zone/writing/ai-agents-on-cloudflare-architecture/

Project Think, the Agents SDK, the AI SDK, Workflows, MCP servers and a plain Worker work at different layers.

- The AI SDK is a library for calling models.
- The Agents SDK is the runtime that keeps an agent alive: state, wakeups, crash recovery.
- Think and AIChatAgent are harnesses that run the loop on top of that runtime.
- Workflows and MCP sit beside the agent.

## 1. Decision tree: what should I build?

The coloured boxes are the recommendations. Section 6 covers combinations.

*Diagram: Decision tree for choosing a Cloudflare AI architecture. Does the model choose the next step itself, looping over tools?*

1. The model does not choose the path. For several fixed steps that need retries or human approval, use a **Workflow**. Otherwise use a **plain Worker** with one model call.
2. The model loops over tools and you already use a harness such as Claude Code, opencode or Codex. Keep it and secure it (see A4 in [Part A](https://kai.zone/writing/cloudflare-ai-choose-your-path/)). If that harness needs your tools or data, expose them with an **MCP server** behind a portal.
3. You want a finished platform that you adapt. Use **Cloudflare OS** for a staff workspace (Part A, A6) or **VibeSDK** for a platform where users build apps (Part A, A7).
4. You build your own loop. If you need no memory or wakeups, use a **Worker with the AI SDK**. If you need memory per user, wakeups or tasks longer than a request, and you want a ready-made loop, use **Project Think**. With the same needs, if you want to own the model call and have a chat UI, use **AIChatAgent**. If it is not a chat (inbox, monitor, webhook), use the **Agent class**.

## 2. The stack: which layer is which

AIChatAgent is built on the AI SDK. The docs describe the AI SDK as "what AIChatAgent and the starter template use under the hood". The runtime answers "where does this agent live and how does it stay durable". The harness answers "what does this agent do on each turn".

*Diagram: Layer stack of the Cloudflare agent architecture. The layers: surfaces, harness, runtime, optional gateway, models.*

## 3. Ten architectures, side by side

### A. Plain Worker with Workers AI or the AI SDK

A normal Worker calls a model. It has no state and no runtime.

*Diagram: Client to Worker to model.*

- **Use when:** one prompt gives one answer (summarise, classify, extract, rewrite), or a short tool loop fits in one request.
- **Skip when:** you need memory, wakeups or work that outlives a request.
- **You write:** the handler, prompt, tools and any storage.
- **You get:** the lowest cost and complexity. You can use any AI SDK provider or the `env.AI` binding with no API key.
- **Docs:** [Using AI models](https://developers.cloudflare.com/agents/runtime/operations/using-ai-models/)

### B. Workflow with LLM steps

Code defines the path and the model is one step in it. Each step is saved and retried on its own.

*Diagram: Trigger into a workflow of four steps.*

- **Use when:** the steps are known in advance (onboarding, document pipelines, deploys, reports) and you want retries per step and human approval.
- **Skip when:** the model has to decide the path. Then you need an agent.
- **You write:** the step code, the workflow class and its config.
- **You get:** per-step retries with backoff and a built-in `waitForApproval()`.
- **Docs:** [Agents with Workflows](https://developers.cloudflare.com/agents/concepts/workflows/), [Workflows versus agent-internal patterns](https://developers.cloudflare.com/agents/concepts/agentic-patterns/long-running-agents/#when-to-use-workflows-vs-agent-internal-patterns), [Workflows limits](https://developers.cloudflare.com/workflows/reference/limits/)

### C. Agent class, your own harness

The runtime without a built-in loop. You get a durable identity and wakeups, and you write what happens each turn.

*Diagram: Triggers into an agent, which calls a model and tools.*

- **Use when:** you need control of the loop or the wire format, or the agent is not a chat: an inbox processor, a monitor, a reminder bot or a webhook handler.
- **Skip when:** you only need a chat UI with memory. Think or AIChatAgent do that.
- **You write:** the turn logic, prompt building, tool calling and message storage format.
- **You get:** synced state, SQLite, WebSockets, four kinds of schedule, crash recovery and sub-agents.
- **Docs:** [Agents API](https://developers.cloudflare.com/agents/runtime/agents-api/), [Schedule tasks](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/), [Durable execution with fibers](https://developers.cloudflare.com/agents/runtime/execution/durable-execution/)

### D. AIChatAgent

A protocol adapter for chat. It stores messages, streams, resumes and handles tools. You write the model call.

*Diagram: Browser chat over WebSocket to AIChatAgent to a model.*

- **Use when:** you want full control of the model call in a chat product (RAG, several models, custom streaming), or a simple chatbot without memory.
- **Skip when:** you want memory blocks, history search and non-destructive compaction out of the box. Think has these.
- **You write:** about 15 lines: `streamText`, tools, system prompt, return the response.
- **You get:** SQLite message storage, resumable streams, tool support including approvals, durable chat recovery.
- **Docs:** [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/)

### E. Project Think

A harness with a fixed structure. You pick the model, and Think runs the loop, memory, tools and recovery.

*Diagram: Browser to Think, with a model and a sub-agent.*

- **Use when:** you want to ship fast with persistent memory, long conversations, history search, sub-agents or proactive turns from schedules and webhooks.
- **Skip when:** you must own the exact LLM call or message format. Use AIChatAgent or the Agent class. Think is still experimental.
- **You write:** `getModel()`, three lines for a first agent. You override single pieces such as the system prompt or tools later.
- **You get:** a branching message tree, memory blocks the model can write, FTS5 search, non-destructive compaction, workspace tools, extensions and durable recovery.
- **Docs:** [Think](https://developers.cloudflare.com/agents/harnesses/think/), [Harnesses](https://developers.cloudflare.com/agents/harnesses/), [announcement](https://blog.cloudflare.com/project-think/)

### F. MCP server on a Worker

You expose tools, and the client's AI app is the agent. There is no loop on your side.

*Diagram: MCP client to an MCP server Worker to your APIs.*

- **Use when:** any MCP client should use your system, such as an internal API, a firewall or a SaaS. The model and the loop live in the client.
- **Skip when:** you need your own agent with memory and a schedule. Build C, D or E and let it call MCP servers as a client.
- **You write:** tool definitions with good descriptions. Few task-shaped tools work better than many thin ones.
- **You get:** streamable HTTP transport, OAuth or Access for login, testing with MCP Inspector.
- **Docs:** [Build a remote MCP server](https://developers.cloudflare.com/agents/model-context-protocol/guides/remote-mcp-server/), [Handler APIs](https://developers.cloudflare.com/agents/model-context-protocol/apis/handler-api/)

### G. Orchestrator with sub-agents

One agent coordinates and several children do focused work. Each child has its own SQLite and typed RPC.

*Diagram: An orchestrator agent delegating to three sub-agents.*

- **Use when:** work splits into specialised roles, or one parent manages many long-lived child contexts (per customer, project or task).
- **Skip when:** one agent with a few tools is enough. Every extra agent adds coordination and debugging cost.
- **You write:** the delegation logic. Children are classes, called by typed RPC.
- **You get:** isolated storage per child, streaming turns through `chat()`, and children usable as tools with retained runs and abort.
- **Docs:** [Sub-agents](https://developers.cloudflare.com/agents/runtime/execution/sub-agents/), [Agents as tools](https://developers.cloudflare.com/agents/runtime/execution/agent-tools/), [Think sub-agent RPC](https://developers.cloudflare.com/agents/harnesses/think/sub-agents/)

### H. Existing harness, secured

Your team already uses an agent such as Claude Code, opencode, Cursor or Copilot, and you control what goes in and out.

*Diagram: Existing harness with model calls through AI Gateway and tool calls through an MCP portal, behind Access.*

- **Use when:** IT must govern the coding or chat agents your staff already use (keys, data loss, tool access, audit), or you expose your own tools to them.
- **Skip when:** you need your own memory model, proactive wakeups or a customer-facing agent.
- **You write:** policy only: AI Gateway for model requests (gateway token or an Access-protected custom domain), the MCP server portal as remote MCP server, DLP and Gateway rules.
- **You get:** per-user logs and budgets, scanned prompts, tools limited per portal and provider keys in the gateway instead of on laptops. The docs have setup guides for coding agents such as Claude Code, OpenAI Codex and OpenCode.
- **Docs:** [Coding agents through AI Gateway](https://developers.cloudflare.com/ai-gateway/integrations/coding-agents/), [AI Gateway with Access](https://developers.cloudflare.com/ai-gateway/configuration/cloudflare-access/), [MCP server portals](https://developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/mcp-portals/). See A4 in [Part A](https://kai.zone/writing/cloudflare-ai-choose-your-path/).

### I. Cloudflare OS, the finished workspace

An open-source workspace for all staff that you deploy in your own Cloudflare account.

*Diagram: Staff in a browser use an agent workspace, with AI Gateway for models and Gatekeepers for systems, behind Access.*

- **Use when:** you want a workspace for all staff, including people who do not code: agent chat, shared skills, small apps and workflows.
- **Skip when:** you need a supported product today (it is early access), you build AI for your customers, or you need shell-heavy engineering agents.
- **You write:** configuration: Access policies, AI Gateway setup, Gatekeepers, your skills and context.
- **You get:** open source (Apache-2.0) with an agent workspace, gadgets, Gatekeepers, inference through AI Gateway and MCP server portal support. See A6 in [Part A](https://kai.zone/writing/cloudflare-ai-choose-your-path/).
- **Docs:** [launch blog post](https://blog.cloudflare.com/cloudflare-os/), [GitHub](https://github.com/cloudflare/cloudflare-os), [os.cloudflare.app](https://os.cloudflare.app/)

### J. VibeSDK, the app-building platform

An open-source platform where users describe an app and an AI coding agent builds, previews and deploys it.

*Diagram: A user describes an app to a Think agent with a project workspace. The agent calls models through AI Gateway and deploys a preview as a Dynamic Worker with its own app data.*

- **Use when:** people should build apps by describing them: customers inside your product, or internal teams building their own tools.
- **Skip when:** you need to govern coding agents your engineers already use (option H), a workspace for all staff (option I) or a supported product today. The README has no support statement.
- **You write:** deployment and configuration: model providers, the domain, project templates and any agent customisation.
- **You get:** open source (MIT) with a Think-based agent loop, a workspace per project, previews as Dynamic Workers, isolated SQLite storage per generated app, optional version history in Artifacts and model calls through AI Gateway. See A7 in [Part A](https://kai.zone/writing/cloudflare-ai-choose-your-path/).
- **Docs:** [GitHub and README](https://github.com/cloudflare/vibesdk), [setup guide](https://github.com/cloudflare/vibesdk/blob/main/docs/setup.md), [launch post](https://blog.cloudflare.com/deploy-your-own-ai-vibe-coding-platform/), [reference architecture](https://developers.cloudflare.com/reference-architecture/diagrams/ai/ai-vibe-coding-platform/)

## 4. Comparison at a glance

| Option | Memory and state | Who runs the loop | Wakes on its own | Survives a crash or deploy | Code to start | Watch out |
| --- | --- | --- | --- | --- | --- | --- |
| **A. Plain Worker + AI SDK** | None built in. Bring your own storage. | The AI SDK, for example a tool loop capped with `stopWhen`. | No, only when something calls it. | Request scoped. You handle retries. | About 10 lines. | Not an agent with memory. Fine for single calls. |
| **B. Workflow** | Step results persisted by the workflow engine. | Your code defines the steps. The model runs inside a step. | Sleeps and waits inside a run. Started by a trigger or an agent. | Per-step retries with backoff, resume after failure. | A separate class plus config. | The path is fixed in code. |
| **C. Agent class** | SQLite per agent, state synced to clients. | You. | Yes: delayed, date, cron and interval schedules, plus email and webhooks. | Fibers with `stash()` checkpoints and `onFiberRecovered`. | A small class, plus the harness you build. | In-memory variables and timers do not survive eviction. |
| **D. AIChatAgent** | Messages in a flat SQLite table. `maxPersistedMessages` deletes the oldest. | You, inside `onChatMessage`. | Yes, it extends Agent. | Durable chat recovery is always on. | About 15 lines. | Forward `abortSignal` to `streamText` or cancelled calls keep running. |
| **E. Think** | Session tree, memory blocks the model writes, FTS5 search, non-destructive compaction. | Think. You override single hooks. | Yes: scheduled tasks, webhooks through submitted turns. | Durable recovery, stream-stall watchdog. | Three lines: `getModel()`. | Experimental. The API may still change. |
| **F. MCP server** | Stateless handler for new servers. | The client, for example Claude or Cursor. | No. | Stateless, so nothing to recover. | A template or the `mcp-worker` example. | Needs auth. Quick-deploy templates still use the deprecated `McpAgent`. |
| **G. Sub-agents** | Own SQLite per child, isolated. | The parent delegates, children run their own turns. | Children can schedule. The parent owns the physical alarm. | Fibers work inside children too. | A class per role. | More agents mean more coordination to debug. |
| **H. Existing harness, secured** | Whatever the existing tool keeps. Policy and logs live in the gateway. | The existing agent. | Depends on the tool. The controls do not add wakeups. | Not provided by the controls. | Policy, not code. | Controls only see traffic routed through the gateway or portal. Block direct provider endpoints too. |
| **I. Cloudflare OS** | Each workspace is a Durable Object. Each gadget has its own SQLite database. | Its built-in Code Mode agent. | Workflows run on demand, on a schedule or on events. | Not stated in the public sources. | Configuration, not the agent. | Early access with rough edges. The managed dashboard version is planned. |
| **J. VibeSDK** | A Durable Object workspace per project. Each generated app has its own SQLite storage. | Its Think agent. | Not stated in the README. | Not stated in the README. | Deployment and configuration. | Reference implementation with no support statement. Builds on experimental and beta features. |

## 5. How to create each one

These are the smallest working setups per option. The snippets come from the linked docs or are adapted from them, and I have not deployed every one. At the time of writing, `@cloudflare/think` 0.20 and `workers-ai-provider` 4 require AI SDK v7 (`ai` ^7); older Think versions also accepted v6. Keep the AI SDK packages on matching major versions and check the current versions before you install.

### E. Project Think: the fastest start for a stateful chat agent

```sh
npm i @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-provider
```

`src/index.ts`:

```ts
import { Think } from "@cloudflare/think";
import { createWorkersAI } from "workers-ai-provider";
import { routeAgentRequest } from "agents";

export class MyAgent extends Think<Env> {
  getModel() {
    return createWorkersAI({ binding: this.env.AI })("@cf/moonshotai/kimi-k2.6");
  }
}

export default {
  async fetch(request: Request, env: Env) {
    return (await routeAgentRequest(request, env)) || new Response("Not found", { status: 404 });
  },
} satisfies ExportedHandler<Env>;
```

`wrangler.jsonc`:

```jsonc
{
  "compatibility_date": "2026-09-30",
  "compatibility_flags": ["nodejs_compat"],
  "ai": { "binding": "AI" },
  "durable_objects": { "bindings": [{ "class_name": "MyAgent", "name": "MyAgent" }] },
  "migrations": [{ "new_sqlite_classes": ["MyAgent"], "tag": "v1" }],
  "observability": { "traces": { "enabled": true } } // optional: traces in the Agents dashboard view
}
```

On the client, use `useAgent({ agent: "MyAgent" })` with `useAgentChat({ agent })` from `@cloudflare/ai-chat/react`. Then read the [Think getting started guide](https://developers.cloudflare.com/agents/harnesses/think/getting-started/) and the [assistant example](https://github.com/cloudflare/agents/tree/main/examples/assistant).

### D. AIChatAgent: when you want to own the model call

```sh
npm i @cloudflare/ai-chat agents ai @ai-sdk/react workers-ai-provider
```

```ts
import { AIChatAgent } from "@cloudflare/ai-chat";
import { createWorkersAI } from "workers-ai-provider";
import { streamText, convertToModelMessages } from "ai";

export class ChatAgent extends AIChatAgent {
  async onChatMessage(_onFinish, options) {
    const workersai = createWorkersAI({ binding: this.env.AI });
    const result = streamText({
      model: workersai("@cf/zai-org/glm-4.7-flash"),
      system: "You are a helpful assistant.",
      messages: await convertToModelMessages(this.messages),
      abortSignal: options?.abortSignal, // forward it, or cancelled calls keep running
    });
    return result.toUIMessageStreamResponse();
  }
}
```

The `new_sqlite_classes` migration is required, with the same wrangler shape as above and `ChatAgent` as the class name. Docs: [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/).

### C. Agent class with a wakeup, for background agents

```ts
import { Agent } from "agents";

export class ReminderAgent extends Agent {
  async onRequest(request) {
    // run sendReminder in 30 seconds, survives hibernation
    await this.schedule(30, "sendReminder", { message: "Check your email" });
    // every day at 08:00 UTC
    await this.schedule("0 8 * * *", "dailyDigest", {});
    return new Response("Scheduled!");
  }
  async sendReminder(payload) { console.log(payload.message); }
  async dailyDigest() { /* build and send the digest */ }
}
```

Pass `{ idempotent: true }` as the fourth argument when you call `schedule()` from `onStart()`, so restarts do not create duplicates. For long work use `runFiber()` with `ctx.stash()`. Docs: [Schedule tasks](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/), [Durable execution](https://developers.cloudflare.com/agents/runtime/execution/durable-execution/), [Quick start](https://developers.cloudflare.com/agents/getting-started/quick-start/).

### A. Plain Worker with the AI SDK

```sh
npm i ai workers-ai-provider
```

```ts
import { generateText } from "ai";
import { createWorkersAI } from "workers-ai-provider";

export default {
  async fetch(request: Request, env: Env) {
    const workersai = createWorkersAI({ binding: env.AI });
    const { text } = await generateText({
      model: workersai("@cf/zai-org/glm-4.7-flash"),
      prompt: "Summarise this ticket in two sentences: ...",
    });
    return Response.json({ text });
  },
};
```

This needs only `"ai": { "binding": "AI" }` in the Wrangler config. To use OpenAI or Anthropic, swap the provider package (`@ai-sdk/openai`). To route across providers with caching and limits, pass a gateway option when calling `env.AI.run`. Docs: [Using AI models](https://developers.cloudflare.com/agents/runtime/operations/using-ai-models/).

### F. MCP server

```sh
npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-authless
npx @modelcontextprotocol/inspector@latest   # connect to http://localhost:8788/mcp
npx wrangler@latest deploy
```

That template still uses the deprecated `McpAgent`. For a new server, start from the [mcp-worker example](https://github.com/cloudflare/agents/tree/main/examples/mcp-worker) and `createMcpHandler()`. Add auth with Access or an OAuth provider. Full guide: [Build a remote MCP server](https://developers.cloudflare.com/agents/model-context-protocol/guides/remote-mcp-server/).

### B. Workflow, and starting one from an agent

```ts
export class ProjectManager extends Agent {
  async startDeployment(task) {
    const instanceId = await this.runWorkflow("DEPLOY_WORKFLOW", {
      taskId: task.id, environment: "production",
    });
    // store instanceId in state, then go back to sleep
  }
  async onWorkflowComplete(workflowName, instanceId, result) {
    // the agent wakes up here when the workflow finishes
  }
}
```

The workflow class and its binding are not shown. Take them from [Run Workflows](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/). The snippet is from the [long-running agents](https://developers.cloudflare.com/agents/concepts/agentic-patterns/long-running-agents/) page. The Agents docs and the Workflows docs describe step limits differently. For step duration and CPU limits, check the [Workflows limits](https://developers.cloudflare.com/workflows/reference/limits/) page and verify the number you rely on.

## 6. Combinations, and traps to avoid

These combinations work:

- Agent + Workflow: the agent owns its rhythm (reminders, polling, state) and a Workflow handles the heavy pipeline such as deploys. The docs use both in one project manager agent.
- Think + MCP tools: Think agents can use tools from MCP servers. An MCP server you build (F) becomes a tool source for a Think agent (E).
- Orchestrator + sub-agents: a parent Think or Agent delegates to children, each with its own storage.
- Any agent + AI Gateway: route models across providers with caching and rate limits through the shared `ai` binding.
- Code Mode: the model writes one piece of code that calls many tools, run in a sandbox. This helps when an API or MCP server has many tools.

Watch out for these:

- Think is experimental, and the docs mark `runTurn()` as "Experimental: stable in shape, but may evolve before Think graduates out of experimental". Check its status before you promise a customer a production date.
- If the path is known in advance, a Workflow is simpler and more predictable than an agent. The docs define agents as non-deterministic and workflows as linear and deterministic.
- Variables in memory are lost. Only `setState`, `this.sql`, schedules and fiber checkpoints survive eviction.
- For tools that reach a firewall or internal API, pair any option with [Workers VPC](https://developers.cloudflare.com/workers-vpc/) (beta at the time of writing) or a Cloudflare Tunnel.

## Sources

- [What are agents?](https://developers.cloudflare.com/agents/concepts/what-are-agents/)
- [Agentic patterns](https://developers.cloudflare.com/agents/concepts/agentic-patterns/)
- [Long-running agents](https://developers.cloudflare.com/agents/concepts/agentic-patterns/long-running-agents/)

The other documentation pages are linked in the option cards above.
