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.
Every example has both a frontend and a backend. This makes them immediately runnable and visually demonstrable — users can npm start and see the feature in action, not just read server logs.
All examples use the Cloudflare Vite plugin for development and builds.
Every example 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
Use start (not dev) for the development server:
{
"scripts": {
"start": "vite dev",
"deploy": "vite build && wrangler deploy",
"types": "wrangler types env.d.ts --include-runtime false"
}
}- Use
wrangler.jsonc(not.toml) - Include
"$schema": "../../node_modules/wrangler/config-schema.json" compatibility_date: "2026-01-28",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
Every example 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:
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.
Include the theme flash prevention script and favicon:
<!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>Extend the base config:
{
"extends": "agents/tsconfig"
}Generated by npx wrangler types. Do not hand-edit. Regenerate when bindings change.
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 (not .dev.vars / .dev.vars.example).
All examples use Kumo (@cloudflare/kumo) for components, with Kumo's default theme (no custom overrides).
- Use Kumo components (
Button,Surface,Text,Badge,Empty, etc.) instead of hand-rolled HTML - Use
@phosphor-icons/reactfor 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 Textdoes not accept aclassNameprop — wrap in a<span>if you need custom classes- Use the
data-modeattribute for dark mode — nodark:Tailwind variants
@import "tailwindcss";
@import "@cloudflare/kumo/styles/tailwind";
@source "../../../node_modules/@cloudflare/kumo/dist/**/*.{js,jsx,ts,tsx}";Every example should include:
PoweredByCloudflare(from@cloudflare/kumo) — footer attribution badge (required in every example)- A dark mode toggle — inline a simple
ModeTogglecomponent usinguseState/useEffect+localStorage+ KumoButtonwith sun/moon icons - Connection status (for WebSocket-based examples) — inline a simple
ConnectionIndicatorshowing a colored dot + label
See /design/visuals.md for detailed Kumo usage patterns and known gaps.
Every example should include an info card at the top of the page explaining what the demo shows. Use the consistent pattern:
<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>Prefer @callable methods + useAgent/agent.call() over manual onRequest/agentFetch or raw WebSocket messages:
// 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"]);- Keep example-specific dependencies minimal — these ship as learning material
- Shared dependencies (
react,vite,wrangler,@cloudflare/vite-plugin, etc.) live in the rootpackage.json - Only add dependencies in the example's
package.jsonif they're specific to what the example demonstrates
Every example needs one. Keep it short:
- One sentence: what this example demonstrates
- How to run it (
npm install && npm start) - Any required env vars
- Code snippets showing the key pattern
- Links to related examples
See TODO.md in this folder for the full checklist.
cross-domain/has avite.config.tsbut does not use@cloudflare/vite-plugin