agentleFS
Sign inSign up

agents / examples

cloudflare/agents/examples/AGENTS.md

Self-contained demo apps that show how to use the Agents SDK. These are user-facing learning material — keep them simple, clear, and consistent. Each example should focus on one feature or concept (e.g., MCP servers, email routing, workflows). The exception is playground/, which is the kitchen-sink showcase covering the full spread of SDK features in a single app. Most examples have both a frontend and a backend. This makes them immediately runnable and visually demonstrable — users can pnpm run…

AGENTS.md5.7k starsChanged 3 months ago
  • Reads credentials
  • Installs packages
# AGENTS.md — examples/

Self-contained demo apps that show how to use the Agents SDK. These are user-facing learning material — keep them simple, clear, and consistent.

Each example should focus on **one feature or concept** (e.g., MCP servers, email routing, workflows). The exception is `playground/`, which is the kitchen-sink showcase covering the full spread of SDK features in a single app.

## Example shape

Most examples have both a frontend and a backend. This makes them immediately runnable and visually demonstrable — users can `pnpm run start` and see the feature in action, not just read server logs.

Focused protocol examples can be server-only when a frontend would obscure the concept. Keep MCP server examples minimal when they demonstrate Cloudflare Workers setup, raw transports, or elicitation behavior that must be exercised from an MCP client.

Early-access examples for a coordinated PR stack live under `examples/next/`.
Each runnable child is an independent workspace package matched by
`examples/next/*`. Harness examples are grouped under
`examples/next/harnesses/*`; `examples/next/README.md` records the stack order.
Move an example into the main catalog only after its public API stabilizes.

Full-stack examples use the [Cloudflare Vite plugin](https://developers.cloudflare.com/workers/vite-plugin/) for development and builds. Server-only examples use Wrangler directly.

Framework-specific examples that intentionally demonstrate a non-React client
may replace the React Vite plugin with that framework's plugin. They must not
install React-only UI dependencies such as Kumo or Streamdown merely to match
the default example shell; accessible framework-native markup and focused CSS
are appropriate for these examples. All other structural conventions still
apply.

## Required structure

Full-stack examples must have:

```
example-name/
  package.json          # name, dependencies, scripts
  vite.config.ts        # must use @cloudflare/vite-plugin
  wrangler.jsonc        # Workers config (not .toml)
  tsconfig.json         # must extend agents/tsconfig
  index.html            # Vite entry point
  README.md             # What this example demonstrates, how to run it
  public/
    favicon.ico         # Cloudflare favicon
  src/
    server.ts           # Worker entry point
    client.tsx          # React client entry
    styles.css          # Kumo + Tailwind imports
```

Server-only examples must have:

```
example-name/
  package.json          # name, dependencies, scripts
  wrangler.jsonc        # Workers config (not .toml)
  tsconfig.json
  README.md             # What this example demonstrates, how to run it
  src/
    index.ts            # Worker entry point
```

## Conventions

### Scripts

Full-stack examples use `start` (not `dev`) for the development server:

```json
{
  "scripts": {
    "start": "vite dev",
    "deploy": "vite build && wrangler deploy",
    "types": "wrangler types env.d.ts --include-runtime false"
  }
}
```

Server-only examples can use Wrangler directly:

```json
{
  "scripts": {
    "dev": "wrangler dev",
    "deploy": "wrangler deploy",
    "types": "wrangler types env.d.ts --include-runtime false"
  }
}
```

### wrangler.jsonc

- Use `wrangler.jsonc` (not `.toml`)
- Include `"$schema": "./node_modules/wrangler/config-schema.json"` — relative to the example root so it resolves both in the workspace and when the example is copied out and installed standalone
- `compatibility_date: "2026-06-11"`, `compatibility_flags: ["nodejs_compat"]`
- Full-stack apps with client routing: add `"assets": { "not_found_handling": "single-page-application" }`
- Use `"run_worker_first"` to route API/agent paths to the Worker
- Do not set `"directory"` in assets — the Vite plugin handles this

### vite.config.ts

Full-stack examples must use the React, Cloudflare, and Tailwind Vite plugins. Examples that use `@callable()` or other decorators must also include the `agents()` plugin from `agents/vite`:

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

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

The `agents()` plugin handles TC39 decorator transforms (Oxc doesn't support them yet). It's safe to include even if the example doesn't use decorators.

### index.html

Include the theme flash prevention script and favicon:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="icon" href="/favicon.ico" />
    <title>Example Title</title>
    <script>
      (() => {
        const stored = localStorage.getItem("theme");
        const mode = stored || "light";
        document.documentElement.setAttribute("data-mode", mode);
        document.documentElement.style.colorScheme = mode;
      })();
    </script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/client.tsx"></script>
  </body>
</html>
```

### tsconfig.json

Extend the base config:

```json
{
  "extends": "agents/tsconfig"
}
```

### env.d.ts

Generated by `pnpm exec wrangler types`. Do not hand-edit. Regenerate when bindings change.

### .env.example

If the example needs secrets (API keys, etc.), include a `.env.example` showing what keys are needed:

```
OPENAI_API_KEY=your-key-here
```

Never commit actual secrets. Use `.env` / `.env.example`.

### UI — Kumo

Full-stack examples use [Kumo](https://kumo-ui.com/) (`@cloudflare/kumo`) for components, with Kumo's default theme (no custom overrides).

#### Kumo basics

- Use Kumo components (`Button`, `Surface`, `Text`, `Badge`, `Empty`, etc.) instead of hand-rolled HTML
- Use `@phosphor-icons/react` for icons (Kumo's icon library)
- Use Kumo semantic color tokens (`text-kumo-default`, `bg-kumo-base`, `border-kumo-line`, etc.) instead of raw Tailwind colors
- `Text` does not accept a `className` prop — wrap in a `<span>` if you need custom classes
- Use the `data-mode` attribute for dark mode — no `dark:` Tailwind variants

#### CSS imports (in `src/styles.css`)

```css
@import "tailwindcss";
@import "@cloudflare/kumo/styles/tailwind";
/* Tailwind ignores node_modules by default, so we source Kumo for class extraction. */
@source "../node_modules/@cloudflare/kumo/dist/**/*.{js,jsx,ts,tsx}";
```

The `@source` path is relative to `src/styles.css`, so it points at the example's own `node_modules` (`../node_modules`). That resolves both in the pnpm workspace (each package gets its own `node_modules` with symlinks) and when the example is copied out and installed standalone — don't use a monorepo-root-relative path like `../../../node_modules`.

#### Required UI patterns

Every full-stack example should include:

- **`PoweredByCloudflare`** (from `@cloudflare/kumo`) — footer attribution badge (**required in every example**)
- **A dark mode toggle** — inline a simple `ModeToggle` component using `useState`/`useEffect` + `localStorage` + Kumo `Button` with sun/moon icons
- **Connection status** (for WebSocket-based examples) — inline a simple `ConnectionIndicator` showing a colored dot + label

See `/design/visuals.md` for detailed Kumo usage patterns and known gaps.

#### Explainer section

Every example should include an info card at the top of the page explaining what the demo shows. Use the consistent pattern:

```tsx
<Surface className="p-4 rounded-xl ring ring-kumo-line">
  <div className="flex gap-3">
    <InfoIcon
      size={20}
      weight="bold"
      className="text-kumo-accent shrink-0 mt-0.5"
    />
    <div>
      <Text size="sm" bold>
        Title
      </Text>
      <span className="mt-1 block">
        <Text size="xs" variant="secondary">
          Description of what this demo shows and how to use it.
        </Text>
      </span>
    </div>
  </div>
</Surface>
```

### Chat message rendering

Any example with a chat UI (`useAgentChat` from `@cloudflare/ai-chat/react` or `@cloudflare/think/react`, or `useChat`) must render the full shape of an assistant turn — not just text. Reference implementations: **`examples/ai-chat`** (streamdown + complete tool states) and **`examples/assistant`** (adds reasoning + approvals + branches).

Rules:

1. **Render `message.parts` in array order (chronological).** Walk the parts with a single `message.parts.map(...)` and branch on `part.type` inside it. Never split parts into separate buckets (e.g. all text, then all tools) — that reorders the turn and hides which tool ran between which sentences.
2. **Assistant text → `streamdown`.** Render assistant text parts with `<Streamdown>` (markdown, code highlighting). User messages stay plain text (`getMessageText(message)`) — user input is rarely markdown.
3. **Reasoning traces.** Render `part.type === "reasoning"` parts as a distinct, muted "Reasoning"/"Thinking" block (reasoning is on by default for reasoning-capable models).
4. **Tool parts — render input, output, AND errors:**
   - **Input** for every tool (not just a `code` field) — render it while the call is still streaming (`input-streaming` / `input-available`) with a running indicator, and after it resolves.
   - **Output** on `output-available`.
   - **Errors** on `output-error` — show `part.errorText` in a clearly-distinct (red) block. Don't let failed tools render as nothing.
   - Handle `approval-requested` / `output-denied` where the example uses tool approval.
5. **Avoid empty bubbles while streaming.** Text and reasoning parts arrive as `{ state: "streaming", text: "" }` before the first delta — gate on `part.text.length > 0 || part.state === "streaming"`.

#### Streamdown setup

`streamdown` (+ `@streamdown/code`) are already repo dependencies. To add markdown rendering to an example:

```jsonc
// package.json dependencies
"streamdown": "^2.5.0",
"@streamdown/code": "^1.1.1"
```

```tsx
import { Streamdown } from "streamdown";
import { code } from "@streamdown/code";

<Streamdown
  className="sd-theme"
  plugins={{ code }}
  controls={false}
  isAnimating={isLastAssistant && isLastTextPart && isStreaming}
>
  {part.text}
</Streamdown>;
```

Tailwind ignores `node_modules`, so `src/styles.css` needs `@source` lines plus the `.sd-theme` bridge that maps Streamdown's tokens to Kumo. Copy the `@source ".../streamdown/..."` / `@source ".../@streamdown/code/..."` lines and the `.sd-theme { ... }` block from `examples/assistant/src/styles.css` verbatim.

### Agent communication

Prefer `@callable` methods + `useAgent`/`agent.call()` over manual `onRequest`/`agentFetch` or raw WebSocket messages:

```typescript
// server.ts
export class MyAgent extends Agent {
  @callable()
  async doSomething(arg: string) {
    return { result: arg };
  }
}

// client.tsx
const agent = useAgent({ agent: "my-agent", name: sessionId });
const result = await agent.call("doSomething", ["hello"]);
```

### Dependencies

- Keep example-specific dependencies minimal — these ship as learning material
- Shared dependencies (`react`, `vite`, `wrangler`, `@cloudflare/vite-plugin`, etc.) live in the root `package.json`
- Only add dependencies in the example's `package.json` if they're specific to what the example demonstrates

### README.md

Every example needs one. Keep it short:

1. One sentence: what this example demonstrates
2. How to run it (`pnpm install && pnpm run start` for full-stack examples, `pnpm install && pnpm run dev` for server-only examples)
3. Any required env vars
4. Code snippets showing the key pattern
5. Links to related examples

## Known issues to clean up

See `TODO.md` in this folder for the full checklist.

- `cross-domain/` has a `vite.config.ts` but does not use `@cloudflare/vite-plugin`

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.