Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build stateful AI agents using the Cloudflare Agents SDK. Load when creating agents with persistent state, scheduling, RPC, MCP servers, email handling, or streaming chat. Covers Agent class, AIChatAgent, state management, and Code Mode for reduced token usage.
.claude/skills/majiayu000-agents-sdk/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | -5% | 0% |
| case-14 | ✗→✓ | ▲ Improved | -19% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 9% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 21% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 0% | 0% |
Build persistent, stateful AI agents on Cloudflare Workers using the agents npm package.
bashnpm install agents
Agents require a binding in wrangler.jsonc:
jsonc{ "durable_objects": { // "class_name" must match your Agent class name exactly "bindings": [{ "name": "Counter", "class_name": "Counter" }] }, "migrations": [ // Required: list all Agent classes for SQLite storage { "tag": "v1", "new_sqlite_classes": ["Counter"] } ] }
| Use Case | Base Class | Package | |----------|------------|---------| | Custom state + RPC, no chat | Agent | agents | | Chat with message persistence | AIChatAgent | @cloudflare/ai-chat | | Building an MCP server | McpAgent | agents/mcp |
| Task | API | |------|-----| | Persist state | this.setState({ count: 1 }) | | Read state | this.state.count | | Schedule task | this.schedule(60, "taskMethod", payload) | | Schedule cron | this.schedule("0 * * * *", "hourlyTask") | | Cancel schedule | this.cancelSchedule(id) | | Queue task | this.queue("processItem", payload) | | SQL query | this.sqlSELECT * FROM users WHERE id = ${id} | | RPC method | @callable() async myMethod() { ... } | | Streaming RPC | @callable({ streaming: true }) async stream(res) { ... } |
typescriptimport { Agent, routeAgentRequest, callable } from "agents"; type State = { count: number }; export class Counter extends Agent<Env, State> { initialState = { count: 0 }; @callable() increment() { this.setState({ count: this.state.count + 1 }); return this.state.count; } } export default { fetch: (req, env) => routeAgentRequest(req, env) ?? new Response("Not found", { status: 404 }) };
Use AIChatAgent for chat with automatic message persistence and resumable streaming.
Install additional dependencies first:
bashnpm install @cloudflare/ai-chat ai @ai-sdk/openai
Add wrangler.jsonc config (same pattern as base Agent):
jsonc{ "durable_objects": { "bindings": [{ "name": "Chat", "class_name": "Chat" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["Chat"] }] }
typescriptimport { AIChatAgent } from "@cloudflare/ai-chat"; import { routeAgentRequest } from "agents"; import { streamText, convertToModelMessages } from "ai"; import { openai } from "@ai-sdk/openai"; export class Chat extends AIChatAgent<Env> { async onChatMessage(onFinish) { const result = streamText({ model: openai("gpt-4o"), messages: await convertToModelMessages(this.messages), onFinish }); return result.toUIMessageStreamResponse(); } } export default { fetch: (req, env) => routeAgentRequest(req, env) ?? new Response("Not found", { status: 404 }) };
Client (React):
tsximport { useAgent } from "agents/react"; import { useAgentChat } from "@cloudflare/ai-chat/react"; const agent = useAgent({ agent: "Chat", name: "my-chat" }); const { messages, input, handleSubmit } = useAgentChat({ agent });
Code Mode generates executable JavaScript instead of making individual tool calls. Use it when:
See references/codemode.md for setup and examples.
streamText and toUIMessageStreamResponse() for chatAgent<Env, State> ensures type safety for this.stateschedule() for time-based, queue() for sequential processingOther measured skills in the registry, with their headline benchmark lift.