Agents, Workers - Agents SDK v0.7.0: Observability rewrite, keepAlive, and waitForMcpConnections
4mo ago
Source
CloudflareAgents, Workers - Agents SDK v0.7.0: Observability rewrite, keepAlive, and waitForMcpConnectionscloudflare.comThe latest release of the Agents SDK 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 โ 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: JavaScript 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 () ; TypeScript 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 โ no subscription code needed in the agent itself: JavaScript 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 ) ; } } }, }; TypeScript 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 . 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. JavaScript const dispose = await this . keepAlive () ; try { const result = await longRunningComputation () ; await sendResults ( result ) ; } finally { dispose () ; } TypeScript 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: JavaScript const result = await this . keepAliveWhile ( async () => { const data = await longRunningComputation () ; return data ; } ) ; TypeScript 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 . 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. JavaScript export class ChatAgent extends AIChatAgent { // Default โ waits up to 10 seconds // waitForMcpConnections = { timeout: 10_000 }; // Wait forever waitForMcpConnections = true ; // Disable waiting waitForMcpConnections = false ; } TypeScript 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: npm i agents@latest @cloudflare/ai-chat@latest
You might also wanna read
MCP as Observability Interface: Connecting AI Agents to Kernel Tracepoints
ingero.ioยท2mo ago
Realtime agent demo
vimeo.comยท11mo ago
UTCP Agent: Lightweight Tool-Calling Protocol for AI Agents
UTCP Agent is a lightweight alternative to MCP (Model Context Protocol) that enables AI agents to call tools directly with minimal code. The
Workflow SDK: Adding Durability and Reliability to TypeScript Functions
The article introduces Workflow SDK, a tool that adds durability, reliability, and observability to asynchronous JavaScript/TypeScript funct
Agent Client Protocol: A Standard for AI Coding Agent-Editor Communication
The Agent Client Protocol (ACP) is a proposed standard for communication between code editors/IDEs and AI coding agents. It addresses the pr
Agents SDK โ TypeScript
github.comยท11mo ago

Comments
Sign in to join the conversation.
No comments yet. Be the first.