|
| 1 | +--- |
| 2 | +title: "Agents SDK v0.7.0: Observability rewrite, keepAlive, and waitForMcpConnections" |
| 3 | +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." |
| 4 | +products: |
| 5 | + - agents |
| 6 | + - workers |
| 7 | +date: 2026-03-02 |
| 8 | +--- |
| 9 | + |
| 10 | +import { TypeScriptExample } from "~/components"; |
| 11 | + |
| 12 | +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. |
| 13 | + |
| 14 | +## Observability rewrite |
| 15 | + |
| 16 | +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. |
| 17 | + |
| 18 | +Every event has a `type`, `payload`, and `timestamp`. Events are routed to seven named channels: |
| 19 | + |
| 20 | +| Channel | Event types | |
| 21 | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 22 | +| `agents:state` | `state:update` | |
| 23 | +| `agents:rpc` | `rpc`, `rpc:error` | |
| 24 | +| `agents:message` | `message:request`, `message:response`, `message:clear`, `message:cancel`, `message:error`, `tool:result`, `tool:approval` | |
| 25 | +| `agents:schedule` | `schedule:create`, `schedule:execute`, `schedule:cancel`, `schedule:retry`, `schedule:error`, `queue:retry`, `queue:error` | |
| 26 | +| `agents:lifecycle` | `connect`, `destroy` | |
| 27 | +| `agents:workflow` | `workflow:start`, `workflow:event`, `workflow:approved`, `workflow:rejected`, `workflow:terminated`, `workflow:paused`, `workflow:resumed`, `workflow:restarted` | |
| 28 | +| `agents:mcp` | `mcp:client:preconnect`, `mcp:client:connect`, `mcp:client:authorize`, `mcp:client:discover` | |
| 29 | + |
| 30 | +Use the typed `subscribe()` helper from `agents/observability` for type-safe access: |
| 31 | + |
| 32 | +<TypeScriptExample> |
| 33 | + |
| 34 | +```ts |
| 35 | +import { subscribe } from "agents/observability"; |
| 36 | + |
| 37 | +const unsub = subscribe("rpc", (event) => { |
| 38 | + if (event.type === "rpc") { |
| 39 | + console.log(`RPC call: ${event.payload.method}`); |
| 40 | + } |
| 41 | + if (event.type === "rpc:error") { |
| 42 | + console.error( |
| 43 | + `RPC failed: ${event.payload.method} — ${event.payload.error}`, |
| 44 | + ); |
| 45 | + } |
| 46 | +}); |
| 47 | + |
| 48 | +// Clean up when done |
| 49 | +unsub(); |
| 50 | +``` |
| 51 | + |
| 52 | +</TypeScriptExample> |
| 53 | + |
| 54 | +In production, all diagnostics channel messages are automatically forwarded to [Tail Workers](/workers/observability/tail-workers/) — no subscription code needed in the agent itself: |
| 55 | + |
| 56 | +<TypeScriptExample> |
| 57 | + |
| 58 | +```ts |
| 59 | +export default { |
| 60 | + async tail(events) { |
| 61 | + for (const event of events) { |
| 62 | + for (const msg of event.diagnosticsChannelEvents) { |
| 63 | + // msg.channel is "agents:rpc", "agents:workflow", etc. |
| 64 | + console.log(msg.timestamp, msg.channel, msg.message); |
| 65 | + } |
| 66 | + } |
| 67 | + }, |
| 68 | +}; |
| 69 | +``` |
| 70 | + |
| 71 | +</TypeScriptExample> |
| 72 | + |
| 73 | +The custom `Observability` override interface is still supported for users who need to filter or forward events to external services. |
| 74 | + |
| 75 | +For the full event reference, refer to the [Observability documentation](/agents/api-reference/observability/). |
| 76 | + |
| 77 | +## `keepAlive()` and `keepAliveWhile()` |
| 78 | + |
| 79 | +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. |
| 80 | + |
| 81 | +`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. |
| 82 | + |
| 83 | +<TypeScriptExample> |
| 84 | + |
| 85 | +```ts |
| 86 | +const dispose = await this.keepAlive(); |
| 87 | +try { |
| 88 | + const result = await longRunningComputation(); |
| 89 | + await sendResults(result); |
| 90 | +} finally { |
| 91 | + dispose(); |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +</TypeScriptExample> |
| 96 | + |
| 97 | +`keepAliveWhile()` wraps an async function with automatic cleanup — the heartbeat starts before the function runs and stops when it completes: |
| 98 | + |
| 99 | +<TypeScriptExample> |
| 100 | + |
| 101 | +```ts |
| 102 | +const result = await this.keepAliveWhile(async () => { |
| 103 | + const data = await longRunningComputation(); |
| 104 | + return data; |
| 105 | +}); |
| 106 | +``` |
| 107 | + |
| 108 | +</TypeScriptExample> |
| 109 | + |
| 110 | +Key details: |
| 111 | + |
| 112 | +- **Multiple concurrent callers** — Each `keepAlive()` call returns an independent disposer. Disposing one does not affect others. |
| 113 | +- **AIChatAgent built-in** — `AIChatAgent` automatically calls `keepAlive()` during streaming responses. You do not need to add it yourself. |
| 114 | +- **Uses the scheduling system** — The heartbeat does not conflict with your own schedules. It shows up in `getSchedules()` if you need to inspect it. |
| 115 | + |
| 116 | +:::note |
| 117 | +`keepAlive()` is marked `@experimental` and may change between releases. |
| 118 | +::: |
| 119 | + |
| 120 | +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). |
| 121 | + |
| 122 | +## `waitForMcpConnections` |
| 123 | + |
| 124 | +`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. |
| 125 | + |
| 126 | +<TypeScriptExample> |
| 127 | + |
| 128 | +```ts |
| 129 | +export class ChatAgent extends AIChatAgent { |
| 130 | + // Default — waits up to 10 seconds |
| 131 | + // waitForMcpConnections = { timeout: 10_000 }; |
| 132 | + |
| 133 | + // Wait forever |
| 134 | + waitForMcpConnections = true; |
| 135 | + |
| 136 | + // Disable waiting |
| 137 | + waitForMcpConnections = false; |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +</TypeScriptExample> |
| 142 | + |
| 143 | +| Value | Behavior | |
| 144 | +| --------------------- | --------------------------------------------- | |
| 145 | +| `{ timeout: 10_000 }` | Wait up to 10 seconds (default) | |
| 146 | +| `{ timeout: N }` | Wait up to `N` milliseconds | |
| 147 | +| `true` | Wait indefinitely until all connections ready | |
| 148 | +| `false` | Do not wait (old behavior before 0.2.0) | |
| 149 | + |
| 150 | +For lower-level control, call `this.mcp.waitForConnections()` directly inside `onChatMessage` instead. |
| 151 | + |
| 152 | +## Other improvements |
| 153 | + |
| 154 | +- **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). |
| 155 | +- **`callbackHost` optional for non-OAuth servers** — `addMcpServer` no longer requires `callbackHost` when connecting to MCP servers that do not use OAuth. |
| 156 | +- **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. |
| 157 | +- **Custom denial messages** — `addToolOutput` now supports `state: "output-error"` with `errorText` for custom denial messages in human-in-the-loop tool approval flows. |
| 158 | +- **`requestId` in chat options** — `onChatMessage` options now include a `requestId` for logging and correlating events. |
| 159 | + |
| 160 | +### Upgrade |
| 161 | + |
| 162 | +To update to the latest version: |
| 163 | + |
| 164 | +```sh |
| 165 | +npm i agents@latest @cloudflare/ai-chat@latest |
| 166 | +``` |
0 commit comments