diff --git a/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx b/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx new file mode 100644 index 00000000000..9e7eec60dde --- /dev/null +++ b/src/content/changelog/agents/2026-03-02-agents-sdk-v0.7.0.mdx @@ -0,0 +1,166 @@ +--- +title: "Agents SDK v0.7.0: Observability rewrite, keepAlive, and waitForMcpConnections" +description: "Agents SDK v0.7.0 replaces the old console.log-based observability with diagnostics_channel events across 7 named channels, adds keepAlive() to prevent idle eviction during long-running work, and introduces waitForMcpConnections to guarantee MCP tools are ready before chat handlers run." +products: + - agents + - workers +date: 2026-03-02 +--- + +import { TypeScriptExample } from "~/components"; + +The latest release of the [Agents SDK](https://github.com/cloudflare/agents) rewrites observability from scratch with `diagnostics_channel`, adds `keepAlive()` to prevent Durable Object eviction during long-running work, and introduces `waitForMcpConnections` so MCP tools are always available when `onChatMessage` runs. + +## Observability rewrite + +The previous observability system used `console.log()` with a custom `Observability.emit()` interface. v0.7.0 replaces it with structured events published to [diagnostics channels](/workers/runtime-apis/nodejs/diagnostics-channel/) — silent by default, zero overhead when nobody is listening. + +Every event has a `type`, `payload`, and `timestamp`. Events are routed to seven named channels: + +| Channel | Event types | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `agents:state` | `state:update` | +| `agents:rpc` | `rpc`, `rpc:error` | +| `agents:message` | `message:request`, `message:response`, `message:clear`, `message:cancel`, `message:error`, `tool:result`, `tool:approval` | +| `agents:schedule` | `schedule:create`, `schedule:execute`, `schedule:cancel`, `schedule:retry`, `schedule:error`, `queue:retry`, `queue:error` | +| `agents:lifecycle` | `connect`, `destroy` | +| `agents:workflow` | `workflow:start`, `workflow:event`, `workflow:approved`, `workflow:rejected`, `workflow:terminated`, `workflow:paused`, `workflow:resumed`, `workflow:restarted` | +| `agents:mcp` | `mcp:client:preconnect`, `mcp:client:connect`, `mcp:client:authorize`, `mcp:client:discover` | + +Use the typed `subscribe()` helper from `agents/observability` for type-safe access: + + + +```ts +import { subscribe } from "agents/observability"; + +const unsub = subscribe("rpc", (event) => { + if (event.type === "rpc") { + console.log(`RPC call: ${event.payload.method}`); + } + if (event.type === "rpc:error") { + console.error( + `RPC failed: ${event.payload.method} — ${event.payload.error}`, + ); + } +}); + +// Clean up when done +unsub(); +``` + + + +In production, all diagnostics channel messages are automatically forwarded to [Tail Workers](/workers/observability/logs/tail-workers/) — no subscription code needed in the agent itself: + + + +```ts +export default { + async tail(events) { + for (const event of events) { + for (const msg of event.diagnosticsChannelEvents) { + // msg.channel is "agents:rpc", "agents:workflow", etc. + console.log(msg.timestamp, msg.channel, msg.message); + } + } + }, +}; +``` + + + +The custom `Observability` override interface is still supported for users who need to filter or forward events to external services. + +For the full event reference, refer to the [Observability documentation](/agents/api-reference/observability/). + +## `keepAlive()` and `keepAliveWhile()` + +Durable Objects are evicted after a period of inactivity (typically 70-140 seconds with no incoming requests, WebSocket messages, or alarms). During long-running operations — streaming LLM responses, waiting on external APIs, running multi-step computations — the agent can be evicted mid-flight. + +`keepAlive()` prevents this by creating a 30-second heartbeat schedule. The alarm firing resets the inactivity timer. Returns a disposer function that cancels the heartbeat when called. + + + +```ts +const dispose = await this.keepAlive(); +try { + const result = await longRunningComputation(); + await sendResults(result); +} finally { + dispose(); +} +``` + + + +`keepAliveWhile()` wraps an async function with automatic cleanup — the heartbeat starts before the function runs and stops when it completes: + + + +```ts +const result = await this.keepAliveWhile(async () => { + const data = await longRunningComputation(); + return data; +}); +``` + + + +Key details: + +- **Multiple concurrent callers** — Each `keepAlive()` call returns an independent disposer. Disposing one does not affect others. +- **AIChatAgent built-in** — `AIChatAgent` automatically calls `keepAlive()` during streaming responses. You do not need to add it yourself. +- **Uses the scheduling system** — The heartbeat does not conflict with your own schedules. It shows up in `getSchedules()` if you need to inspect it. + +:::note +`keepAlive()` is marked `@experimental` and may change between releases. +::: + +For the full API reference and when-to-use guidance, refer to [Schedule tasks — Keeping the agent alive](/agents/api-reference/schedule-tasks/#keeping-the-agent-alive). + +## `waitForMcpConnections` + +`AIChatAgent` now waits for MCP server connections to settle before calling `onChatMessage`. This ensures `this.mcp.getAITools()` returns the full set of tools, especially after Durable Object hibernation when connections are being restored in the background. + + + +```ts +export class ChatAgent extends AIChatAgent { + // Default — waits up to 10 seconds + // waitForMcpConnections = { timeout: 10_000 }; + + // Wait forever + waitForMcpConnections = true; + + // Disable waiting + waitForMcpConnections = false; +} +``` + + + +| Value | Behavior | +| --------------------- | --------------------------------------------- | +| `{ timeout: 10_000 }` | Wait up to 10 seconds (default) | +| `{ timeout: N }` | Wait up to `N` milliseconds | +| `true` | Wait indefinitely until all connections ready | +| `false` | Do not wait (old behavior before 0.2.0) | + +For lower-level control, call `this.mcp.waitForConnections()` directly inside `onChatMessage` instead. + +## Other improvements + +- **MCP deduplication by name and URL** — `addMcpServer` with HTTP transport now deduplicates on both server name and URL. Calling it with the same name but a different URL creates a new connection. URLs are normalized before comparison (trailing slashes, default ports, hostname case). +- **`callbackHost` optional for non-OAuth servers** — `addMcpServer` no longer requires `callbackHost` when connecting to MCP servers that do not use OAuth. +- **MCP URL security** — Server URLs are validated before connection to prevent SSRF. Private IP ranges, loopback addresses, link-local addresses, and cloud metadata endpoints are blocked. +- **Custom denial messages** — `addToolOutput` now supports `state: "output-error"` with `errorText` for custom denial messages in human-in-the-loop tool approval flows. +- **`requestId` in chat options** — `onChatMessage` options now include a `requestId` for logging and correlating events. + +### Upgrade + +To update to the latest version: + +```sh +npm i agents@latest @cloudflare/ai-chat@latest +```