Skip to content

Commit 10ce8bc

Browse files
Sync documentation from cloudflare/agents PR 812
This commit syncs documentation changes from PR 812 which includes: ## New Features Documented ### Scheduling - scheduleEvery() method for fixed-interval recurring tasks - Overlap prevention and error resilience - Comparison between cron and interval scheduling ### MCP Client - Options-based addMcpServer() API (cleaner alternative to positional params) - Transport options configuration - Backward compatibility maintained ### Callable Methods (NEW PAGE) - @callable decorator for WebSocket RPC - Streaming with StreamingResponse.error() method - Client-side RPC timeout support - Method introspection with getCallableMethods() - Options-based streaming API ### Custom Routing (NEW PAGE) - basePath for custom URL routing - Server-sent identity with onIdentity callbacks - Identity change handling on reconnect - Security controls with sendIdentityOnConnect - Instance naming patterns ### State Management - Enhanced onStateUpdate with source parameter - Bidirectional state updates - Vanilla JS AgentClient examples - Best practices for avoiding loops ### WebSockets - Connection management (getConnections, getConnection) - Connection tags and filtering - Per-connection state - Hibernation behavior documentation - Broadcasting patterns ## New Documentation Pages - api-reference/callable-methods.mdx - Complete @callable decorator reference - concepts/routing.mdx - Custom routing and identity management - getting-started/quickstart.mdx - Comprehensive quickstart guide - guides/add-to-existing-project.mdx - Integration with existing Workers ## Updated Pages - api-reference/schedule-tasks.mdx - api-reference/store-and-sync-state.mdx - api-reference/websockets.mdx - model-context-protocol/mcp-client-api.mdx All documentation follows cloudflare-docs style guidelines with proper component usage (TypeScriptExample, WranglerConfig, PackageManagers), full relative links with trailing slashes, and no contractions. Related PR: cloudflare/agents#812 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent c3ba130 commit 10ce8bc

File tree

8 files changed

+2247
-19
lines changed

8 files changed

+2247
-19
lines changed

src/content/docs/agents/api-reference/callable-methods.mdx

Lines changed: 565 additions & 0 deletions
Large diffs are not rendered by default.

src/content/docs/agents/api-reference/schedule-tasks.mdx

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/com
99

1010
An Agent can schedule tasks to be run in the future by calling `this.schedule(when, callback, data)`, where `when` can be a delay, a `Date`, or a cron string; `callback` the function name to call, and `data` is an object of data to pass to the function.
1111

12+
For recurring tasks at fixed intervals, use `this.scheduleEvery(intervalSeconds, callback, data)` to run a task repeatedly every N seconds.
13+
1214
Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read+write state: scheduled tasks can invoke any regular method on your Agent.
1315

1416
:::note[Calling destroy() in scheduled tasks]
@@ -63,19 +65,22 @@ let task = await this.schedule(10, "someTask", { message: "hello" });
6365
// schedule a task to run at a specific date
6466
let task = await this.schedule(new Date("2025-01-01"), "someTask", {});
6567

66-
// schedule a task to run every 10 minutes
68+
// schedule a task to run every 10 minutes (cron)
6769
let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello" });
6870

6971
// schedule a task to run every 10 minutes, but only on Mondays
7072
let task = await this.schedule("*/10 * * * 1", "someTask", { message: "hello" });
7173

74+
// schedule a task to run every 30 seconds (fixed interval)
75+
let task = await this.scheduleEvery(30, "poll", { source: "api" });
76+
7277
// cancel a scheduled task
7378
this.cancelSchedule(task.id);
7479
```
7580

7681
</TypeScriptExample>
7782

78-
Calling `await this.schedule` returns a `Schedule`, which includes the task's randomly generated `id`. You can use this `id` to retrieve or cancel the task in the future. It also provides a `type` property that indicates the type of schedule, for example, one of `"scheduled" | "delayed" | "cron"`.
83+
Calling `await this.schedule` returns a `Schedule`, which includes the task's randomly generated `id`. You can use this `id` to retrieve or cancel the task in the future. It also provides a `type` property that indicates the type of schedule, for example, one of `"scheduled" | "delayed" | "cron" | "interval"`.
7984

8085
:::note[Maximum scheduled tasks]
8186

@@ -156,3 +161,92 @@ export class SchedulingAgent extends Agent {
156161
When `destroy()` is called from within a scheduled task, the Agent SDK defers the destruction to ensure the scheduled callback completes successfully. The Agent instance will be evicted immediately after the callback finishes executing.
157162

158163
:::
164+
165+
### Interval scheduling with scheduleEvery()
166+
167+
Use `this.scheduleEvery()` to run tasks at fixed intervals. This is useful for polling, health checks, or any recurring task that needs sub-minute precision or arbitrary durations that cannot be expressed in cron syntax.
168+
169+
<TypeScriptExample>
170+
171+
```ts
172+
export class PollingAgent extends Agent {
173+
async onRequest(request) {
174+
// Poll an API every 30 seconds
175+
await this.scheduleEvery(30, "poll", { source: "api" });
176+
return new Response("Polling started");
177+
}
178+
179+
async poll(data) {
180+
// This runs every 30 seconds
181+
const response = await fetch("https://api.example.com/data");
182+
const result = await response.json();
183+
// Process data...
184+
}
185+
}
186+
```
187+
188+
</TypeScriptExample>
189+
190+
#### Key differences between cron and interval scheduling
191+
192+
| Feature | Cron (`schedule`) | Interval (`scheduleEvery`) |
193+
| ------------------- | ------------------------------ | ------------------------------ |
194+
| Minimum granularity | 1 minute | 1 second |
195+
| Arbitrary intervals | No (must fit cron pattern) | Yes |
196+
| Fixed schedule | Yes (for example, "every day at 8am") | No (relative to start) |
197+
| Overlap prevention | No | Yes (built-in) |
198+
199+
#### Overlap prevention
200+
201+
If a callback takes longer than the interval, the next execution is skipped to prevent runaway resource usage. A warning is logged when this occurs.
202+
203+
<TypeScriptExample>
204+
205+
```ts
206+
export class DataSyncAgent extends Agent {
207+
async syncData() {
208+
// If this takes 45 seconds and interval is 30 seconds,
209+
// the next sync is skipped (with a warning logged)
210+
const data = await slowExternalApi();
211+
await this.processData(data);
212+
}
213+
}
214+
215+
// Set up 30-second interval
216+
await this.scheduleEvery(30, "syncData", {});
217+
```
218+
219+
</TypeScriptExample>
220+
221+
#### Error resilience
222+
223+
If the callback throws an error, the interval continues. Only that execution fails:
224+
225+
<TypeScriptExample>
226+
227+
```ts
228+
async syncData() {
229+
// Even if this throws, the interval keeps running
230+
const response = await fetch("https://api.example.com/data");
231+
if (!response.ok) throw new Error("Sync failed");
232+
// ...
233+
}
234+
```
235+
236+
</TypeScriptExample>
237+
238+
#### Cancelling interval schedules
239+
240+
To stop an interval schedule, use `cancelSchedule()` with the task ID:
241+
242+
<TypeScriptExample>
243+
244+
```ts
245+
// Start interval
246+
const { id } = await this.scheduleEvery(30, "poll", {});
247+
248+
// Stop interval later
249+
await this.cancelSchedule(id);
250+
```
251+
252+
</TypeScriptExample>

src/content/docs/agents/api-reference/store-and-sync-state.mdx

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ State within an Agent is:
1919

2020
Agent state is stored in a SQL database that is embedded within each individual Agent instance: you can interact with it using the higher-level `this.setState` API (recommended), which allows you to sync state and trigger events on state changes, or by directly querying the database with `this.sql`.
2121

22+
State is:
23+
24+
* **Persistent** - Automatically saved to SQLite, survives restarts and hibernation
25+
* **Synchronized** - Changes broadcast to all connected WebSocket clients instantly
26+
* **Bidirectional** - Both server and clients can update state
27+
* **Type-safe** - Full TypeScript support with generics
28+
2229
#### State API
2330

2431
Every Agent has built-in state management capabilities. You can set and update the Agent's state directly using `this.setState`:
@@ -48,8 +55,17 @@ export class MyAgent extends Agent {
4855
}
4956

5057
// Handle state updates
58+
// source is "server" when this.setState() is called
59+
// source is Connection when a client pushes state updates
5160
onStateUpdate(state, source: "server" | Connection) {
5261
console.log("state updated", state);
62+
63+
// Ignore server-initiated updates to avoid infinite loops
64+
if (source === "server") return;
65+
66+
// A client updated state - validate and process
67+
const connection = source;
68+
console.log(`Client ${connection.id} updated state`);
5369
}
5470
}
5571
```
@@ -127,7 +143,7 @@ Any initial state is synced to clients connecting via [the `useAgent` hook](#syn
127143

128144
Clients can connect to an Agent and stay synchronized with its state using the React hooks provided as part of `agents/react`.
129145

130-
A React application can call `useAgent` to connect to a named Agent over WebSockets at
146+
A React application can call `useAgent` to connect to a named Agent over WebSockets:
131147

132148
<TypeScriptExample>
133149

@@ -145,6 +161,7 @@ function StateInterface() {
145161
});
146162

147163
const increment = () => {
164+
// Client pushes state update to agent
148165
agent.setState({ counter: state.counter + 1 });
149166
};
150167

@@ -159,6 +176,27 @@ function StateInterface() {
159176

160177
</TypeScriptExample>
161178

179+
For vanilla JavaScript applications, use `AgentClient`:
180+
181+
<TypeScriptExample>
182+
183+
```ts
184+
import { AgentClient } from "agents/client";
185+
186+
const client = new AgentClient({
187+
agent: "game-agent",
188+
name: "room-123",
189+
onStateUpdate: (state) => {
190+
document.getElementById("score").textContent = state.score;
191+
}
192+
});
193+
194+
// Push state update
195+
client.setState({ ...client.state, score: 100 });
196+
```
197+
198+
</TypeScriptExample>
199+
162200
The state synchronization system:
163201

164202
* Automatically syncs the Agent's state to all connected clients
@@ -169,10 +207,11 @@ The state synchronization system:
169207
Common use cases:
170208

171209
* Real-time collaborative features
172-
* Multi-window/tab synchronization
210+
* Multi-window or tab synchronization
173211
* Live updates across multiple devices
174212
* Maintaining consistent UI state across clients
175-
* When new clients connect, they automatically receive the current state from the Agent, ensuring all clients start with the latest data.
213+
214+
When new clients connect, they automatically receive the current state from the Agent, ensuring all clients start with the latest data.
176215

177216
### SQL API
178217

0 commit comments

Comments
 (0)