-
#1207
b1da19cThanks @threepointone! - Addtransport: "auto"option forMcpAgent.serve()that serves both Streamable HTTP and legacy SSE on the same endpoint. Capable clients use Streamable HTTP automatically, while older SSE-only clients continue to work transparently. -
#1217
6801966Thanks @threepointone! - update partyserver
- #1201
fc6d214Thanks @threepointone! - Bump@modelcontextprotocol/sdkfrom 1.26.0 to 1.28.0 and populateurlon MCPRequestInfoso tool handlers can access the request URL and query parameters
- #1198
dde826eThanks @threepointone! - DerivecallbackHostfromconnection.uriinaddMcpServerwhen called from a@callablemethod over WebSocket. Previously,callbackHosthad to be passed explicitly (or read from an env var) because the WebSocketonMessagecontext has no HTTP request to derive the host from. Now the host is automatically extracted from the WebSocket connection's original upgrade URL, soaddMcpServer("name", url)works without any extra options in callables. Also addsvite/clientto the sharedagents/tsconfigtypes for TS6 compatibility with CSS side-effect imports.
- #1190
b39dbffThanks @threepointone! - Export sharedagents/tsconfigandagents/viteso examples and internal projects are self-contained. Theagents/viteplugin handles TC39 decorator transforms for@callable()until Oxc lands native support.
- #1182
c03e87bThanks @dmmulroy! - FixelicitInput()hanging on RPC transport by intercepting elicitation responses inhandleMcpMessage()and addingawaitPendingResponse()toRPCServerTransport
- #1181
e9bace9Thanks @threepointone! - Fix alarm handler resilience: moveJSON.parse(row.payload)inside try/catch and guard warning emission so a single failure cannot break processing of remaining schedule rows.
- #1176
750446bThanks @threepointone! - Remove local development workarounds for workflow instance methods now thatpause(),resume(),restart(), andterminate()are supported inwrangler dev
-
#1152
16cc622Thanks @threepointone! - feat: expose readablestateproperty onuseAgentandAgentClientBoth
useAgent(React) andAgentClient(vanilla JS) now expose astateproperty that tracks the current agent state. Previously, state was write-only viasetState()— reading state required manually tracking it through theonStateUpdatecallback.React (useAgent)
const agent = useAgent<GameAgent, GameState>({ agent: "game-agent", name: "room-123", }); // Read state directly — no need for separate useState + onStateUpdate return <div>Score: {agent.state?.score}</div>; // Spread for partial updates — works correctly now agent.setState({ ...agent.state, score: agent.state.score + 10 });
agent.stateis reactive — the component re-renders when state changes from either the server or client-sidesetState().Vanilla JS (AgentClient)
const client = new AgentClient<GameAgent>({ agent: "game-agent", name: "room-123", host: "your-worker.workers.dev", }); // State updates synchronously on setState and server broadcasts client.setState({ score: 100 }); console.log(client.state); // { score: 100 }
Backward compatible
The
onStateUpdatecallback continues to work exactly as before. The newstateproperty is additive — it provides a simpler alternative to manual state tracking for the common case.Type:
State | undefinedState starts as
undefinedand is populated when the server sends state on connect (frominitialState) or whensetState()is called. Use optional chaining (agent.state?.field) for safe access. -
#1154
74a018aThanks @threepointone! - feat: idempotentschedule()to prevent row accumulation across DO restartsschedule()now supports anidempotentoption that deduplicates by(type, callback, payload), preventing duplicate rows from accumulating when called repeatedly (e.g., inonStart()).Cron schedules are idempotent by default. Calling
schedule("0 * * * *", "tick")multiple times with the same callback, cron expression, and payload returns the existing schedule instead of creating a duplicate. Set{ idempotent: false }to override.Delayed and scheduled (Date) types support opt-in idempotency:
async onStart() { // Safe across restarts — only one row exists at a time await this.schedule(60, "maintenance", undefined, { idempotent: true }); }
New warnings for common foot-guns:
schedule()called insideonStart()without{ idempotent: true }now emits aconsole.warnwith actionable guidance (once per callback, skipped for cron and whenidempotentis explicitly set)alarm()processing ≥10 stale one-shot rows for the same callback emits aconsole.warnand aschedule:duplicate_warningdiagnostics channel event
-
#1146
b74e108Thanks @threepointone! - feat: strongly-typedAgentClientwithcallinference andstubproxyAgentClientnow accepts an optional agent type parameter for full type inference on RPC calls, matching the typed experience thatuseAgentalready provides.New: typed
callandstubWhen an agent type is provided,
call()infers method names, argument types, and return types from the agent's methods. A newstubproperty provides a direct RPC-style proxy — call agent methods as if they were local functions:const client = new AgentClient<MyAgent>({ agent: "my-agent", host: window.location.host, }); // Typed call — method name autocompletes, args and return type inferred const value = await client.call("getValue"); // Typed stub — direct RPC-style proxy await client.stub.getValue(); await client.stub.add(1, 2);
State is automatically inferred from the agent type, so
onStateUpdateis also typed:const client = new AgentClient<MyAgent>({ agent: "my-agent", host: window.location.host, onStateUpdate: (state) => { // state is typed as MyAgent's state type }, });
Backward compatible
Existing untyped usage continues to work without changes:
const client = new AgentClient({ agent: "my-agent", host: "..." }); client.call("anyMethod", [args]); // still works client.call<number>("add", [1, 2]); // explicit return type still works client.stub.anyMethod("arg1", 123); // untyped stub also available
The previous
AgentClient<State>pattern is preserved —new AgentClient<{ count: number }>({...})still correctly typesonStateUpdateand leavescall/stubuntyped.Breaking:
callis now an instance property instead of a prototype methodAgentClient.prototype.callno longer exists. Thecallfunction is assigned per-instance in the constructor (via.bind()). This is required for the conditional type system to switch between typed and untyped signatures. Normal usage (client.call(...)) is unaffected, but code that reflects on the prototype or subclasses that overridecallas a method may need adjustment.Shared type utilities
The RPC type utilities (
AgentMethods,AgentStub,RPCMethods, etc.) are now exported fromagents/clientso they can be shared betweenAgentClientanduseAgent, and are available to consumers who need them for advanced typing scenarios. -
#1138
36e2020Thanks @threepointone! - Drop Zod v3 from peer dependency range — now requireszod ^4.0.0. Replace dynamicimport("ai")withz.fromJSONSchema()from Zod 4 for MCP tool schema conversion, removing theairuntime dependency from the agents core. RemoveensureJsonSchema().
-
#1147
1f85b06Thanks @threepointone! - Replace schedule-based keepAlive with lightweight ref-counted alarmskeepAlive()no longer creates schedule rows or emitsschedule:create/schedule:execute/schedule:cancelobservability events — it uses an in-memory ref count and feeds directly into_scheduleNextAlarm()- multiple concurrent
keepAlive()callers now share a single alarm cycle instead of each creating their own interval schedule row - add
_onAlarmHousekeeping()hook (called on every alarm cycle) for extensions like the fiber mixin to run housekeeping without coupling to the scheduling system - bump internal schema to v2 with a migration that cleans up orphaned
_cf_keepAliveHeartbeatschedule rows from the previous implementation - remove
@experimentalfromkeepAlive()andkeepAliveWhile()
- #1128
01cfb52Thanks @threepointone! - AddsessionAffinitygetter toAgentbase class for Workers AI prefix-cache optimization. Returns the Durable Object ID, which is globally unique and stable per agent instance. Pass it as thesessionAffinityoption when creating a Workers AI model to route requests from the same agent to the same backend replica.
- #1122
a16e74dThanks @threepointone! - Removeagents/experimental/workspaceexport.Workspacenow lives in@cloudflare/shell— import it from there instead.
- #1120
6a6108cThanks @whoiskatrin! - Restore lost Durable Object alarms whenscheduleEvery()reuses an existing interval schedule after restart.
-
#1090
a91b598Thanks @threepointone! - Allow MCP clients to connect to localhost and loopback URLs again for local development while continuing to block private, link-local, and metadata endpoints. -
#1088
16e2833Thanks @threepointone! - Embed sub-agent (facet) API into the Agent base class. AddssubAgent(),abortSubAgent(), anddeleteSubAgent()methods directly onAgent, replacing the experimentalwithSubAgentsmixin. Uses composite facet keys for class-aware naming, guards scheduling andkeepAlivein facets, and persists the facet flag to storage so it survives hibernation. -
#1085
0b73a74Thanks @threepointone! - Remove unnecessary storage operations in McpAgent:- Fix redundant
propsread inonStart: skipstorage.get("props")when props are passed directly (only read from storage on hibernation recovery) - Replace elicitation storage polling with in-memory Promise/resolver: eliminates repeated
storage.get/put/deletecalls (up to 6 per elicitation) in favor of zero-storage in-memory signaling
- Fix redundant
-
#1086
e8195e7Thanks @threepointone! - Simplify Agent storage: schema version gating and single-row state- Skip redundant DDL migrations on established DOs by tracking schema version in
cf_agents_state - Eliminate
STATE_WAS_CHANGEDrow — state persistence now uses a single row with row-existence check, correctly handling falsy values (null, 0, false, "") - Clean up legacy
STATE_WAS_CHANGEDrows during migration - Add schema DDL snapshot test that breaks if table definitions change without bumping
CURRENT_SCHEMA_VERSION - Fix corrupted state test helper that was using incorrect row IDs
- Skip redundant DDL migrations on established DOs by tracking schema version in
-
#1081
933b00fThanks @threepointone! - Add default console.error logging to onWorkflowError() so unhandled workflow errors are visible in logs -
#1089
a1eab1dThanks @threepointone! - AddWorkspaceclass — durable file storage for any Agent with hybrid SQLite+R2 backend and optional just-bash shell execution. Usage:new Workspace(this, { r2, idPrefix }). Import fromagents/workspace.
-
#1071
6312684Thanks @threepointone! - Fix missingawaiton_workflow_updateStateRPC calls inAgentWorkflow._wrapStep()forupdateAgentState,mergeAgentState, andresetAgentState, which could cause state updates to be silently lost. -
#1069
b5238deThanks @threepointone! - AddWorkspaceclass — durable file storage for any Agent with hybrid SQLite+R2 backend and optional just-bash shell execution. IncludesBashSessionfor multi-step shell workflows with persistent cwd and env across exec calls, andcwdoption onbash(). Usage:new Workspace(this, { r2, r2Prefix }). Import fromagents/experimental/workspace.
- #1063
4ace1d4Thanks @threepointone! - Fix CHECK constraint migration forcf_agents_schedulestable to include'interval'type, allowingscheduleEvery()andkeepAlive()to work on DOs created with older SDK versions.
-
#1057
c804c73Thanks @threepointone! - Updated dependencies. -
#1057
c804c73Thanks @threepointone! - Fix workflow RPC callbacks bypassing Agent initialization. The_workflow_handleCallback,_workflow_broadcast, and_workflow_updateStatemethods now call__unsafe_ensureInitialized()before executing, ensuringthis.nameis hydrated andonStart()has been called even when the Durable Object wakes via native RPC.
-
#1050
6157741Thanks @ask-bonk! - Fix Agent alarm() bypassing PartyServer's initializationThe Agent class defined
alarmas apublic readonlyarrow function property, which completely shadowed PartyServer'salarm()prototype method. This meant#ensureInitialized()was never called when a Durable Object woke via alarm (e.g. fromscheduleEvery), causingthis.nameto throw andonStartto never run.Converted
alarmfrom an arrow function property to a regular async method that callssuper.alarm()before processing scheduled tasks. Also added anonAlarm()no-op override to suppress PartyServer's default warning log. -
#1052
f1e2bfaThanks @ask-bonk! - MakescheduleEvery()idempotentscheduleEvery()now deduplicates by the combination of callback name, interval, and payload: calling it multiple times with the same arguments returns the existing schedule instead of creating a duplicate. A different interval or payload creates a separate, independent schedule.This fixes the common pattern of calling
scheduleEvery()insideonStart(), which runs on every Durable Object wake. Previously each wake created a new interval schedule, leading to a thundering herd of duplicate executions.
-
#1046
2cde136Thanks @threepointone! - Addagentandnamefields to observability events, identifying which agent class and instance emitted each event.New events:
disconnect(WebSocket close),email:receive,email:reply,queue:create, and a newagents:emailchannel.Make
_emitprotected so subclasses can use it. UpdateAIChatAgentto use_emitso message/tool events carry agent identity.
-
#1024
e9ae070Thanks @threepointone! - Overhaul observability:diagnostics_channel, leaner events, error tracking.BaseEvent: RemovedidanddisplayMessagefields. Events now contain onlytype,payload, andtimestamp. Thepayloadtype is now strict — accessing undeclared fields is a type error. Narrow onevent.typebefore accessing payload properties.Observability.emit(): Removed the optionalctxsecond parameter.AgentObservabilityEvent: Split combined union types so each event has its own discriminant (enables properExtract-based type narrowing). Added new error event types.
If you have a custom
Observabilityimplementation, update youremitsignature toemit(event: ObservabilityEvent): void.The default
genericObservabilityimplementation no longer logs every event to the console. Instead, events are published to named diagnostics channels using the Node.jsdiagnostics_channelAPI. Publishing to a channel with no subscribers is a no-op, eliminating logspam.Seven named channels, one per event domain:
agents:state— state sync eventsagents:rpc— RPC method calls and errorsagents:message— message request/response/clear/cancel/error + tool result/approvalagents:schedule— schedule and queue create/execute/cancel/retry/error eventsagents:lifecycle— connection and destroy eventsagents:workflow— workflow start/event/approve/reject/terminate/pause/resume/restartagents:mcp— MCP client connect/authorize/discover events
Error events are now emitted at failure sites instead of (or alongside)
console.error:rpc:error— RPC method failures (includes method name and error message)schedule:error— schedule callback failures after all retries exhaustedqueue:error— queue callback failures after all retries exhausted
All 20+ inline
emitblocks in the Agent class have been replaced with a private_emit()helper that auto-generates timestamps, reducing each call site from ~10 lines to 1.A new
subscribe()function is exported fromagents/observabilitywith full type narrowing per channel:import { subscribe } from "agents/observability"; const unsub = subscribe("rpc", (event) => { // event is fully typed as rpc | rpc:error console.log(event.payload.method); });
In production, all diagnostics channel messages are automatically forwarded to Tail Workers via
event.diagnosticsChannelEvents— no subscription needed in the agent itself.The
diagnostics_channelAPI also providesTracingChannelfor start/end/error spans withAsyncLocalStorageintegration, opening the door to end-to-end tracing of RPC calls, workflow steps, and schedule executions. -
#1029
c898308Thanks @threepointone! - Add experimentalkeepAlive()andkeepAliveWhile()methods to the Agent class. Keeps the Durable Object alive via alarm heartbeats (every 30 seconds), preventing idle eviction during long-running work.keepAlive()returns a disposer function;keepAliveWhile(fn)runs an async function and automatically cleans up the heartbeat when it completes.AIChatAgentnow automatically callskeepAliveWhile()during_reply()streaming, preventing idle eviction during long LLM generations.
-
#1020
70ebb05Thanks @threepointone! - udpate dependencies -
#1035
24cf279Thanks @threepointone! - MCP protocol handling improvements:- JSON-RPC error responses:
RPCServerTransport.handle()now returns a proper JSON-RPC-32600 Invalid Requesterror response for malformed messages instead of throwing an unhandled exception. This aligns with the JSON-RPC 2.0 spec requirement that servers respond with error objects. - McpAgent protocol message suppression:
McpAgentnow overridesshouldSendProtocolMessages()to suppressCF_AGENT_IDENTITY,CF_AGENT_STATE, andCF_AGENT_MCP_SERVERSframes on MCP transport connections (detected via thecf-mcp-methodheader). Regular WebSocket connections to a hybrid McpAgent are unaffected. - CORS warning removed: Removed the one-time warning about
AuthorizationinAccess-Control-Allow-Headerswith wildcard origin. The warning was noisy and unhelpful — the combination is valid for non-credentialed requests and does not pose a real security risk.
- JSON-RPC error responses:
-
#996
baf6751Thanks @threepointone! - Fix race condition where MCP tools are intermittently unavailable in onChatMessage after hibernation.agents: AddedMCPClientManager.waitForConnections(options?)which awaits all in-flight connection and discovery operations. Accepts an optional{ timeout }in milliseconds. Background restore promises fromrestoreConnectionsFromStorage()are now tracked so callers can wait for them to settle.@cloudflare/ai-chat: AddedwaitForMcpConnectionsopt-in config onAIChatAgent. Set totrueto wait indefinitely, or{ timeout: 10_000 }to cap the wait. Default isfalse(non-blocking, preserving existing behavior). For lower-level control, callthis.mcp.waitForConnections()directly in youronChatMessage. -
#1035
24cf279Thanks @threepointone! - Fixthis.sqlto throwSqlErrordirectly instead of routing throughonErrorPreviously, SQL errors from
this.sqlwere passed tothis.onError(), which by default logged the error and re-threw it. This caused confusing double error logs and made it impossible to catch SQL errors with a simple try/catch aroundthis.sqlcalls ifonErrorwas overridden to swallow errors.Now,
this.sqlwraps failures inSqlError(which includes the query string for debugging) and throws directly. TheonErrorlifecycle hook is reserved for WebSocket connection errors and unhandled server errors, not SQL errors. -
#1022
c2bfd3cThanks @threepointone! - Remove redundant unawaitedupdatePropscalls in MCP transport handlers that caused sporadic "Failed to pop isolated storage stack frame" errors in test environments. Props are already delivered throughgetAgentByName→onStart, making the extra calls unnecessary. Also removes the RPC experimental warning fromaddMcpServer. -
#1003
d24936cThanks @threepointone! - Fix:throw new Error()in AgentWorkflow now triggersonWorkflowErroron the AgentPreviously, throwing an error inside a workflow's
run()method would halt the workflow but never notify the Agent viaonWorkflowError. Only explicitstep.reportError()calls triggered the callback, but those did not halt the workflow.Now, unhandled errors in
run()are automatically caught and reported to the Agent before re-throwing. A double-notification guard (_errorReportedflag) ensures that ifstep.reportError()was already called before the throw, the auto-report is skipped. -
#1040
766f20bThanks @threepointone! - ChangedaddMcpServerdedup logic to match on both server name AND URL for HTTP transport. Previously, callingaddMcpServerwith the same name but a different URL would silently return the stale connection. Now each unique (name, URL) pair is treated as a separate connection. RPC transport continues to dedup by name only. -
#997
a570ea5Thanks @threepointone! - Security hardening for Agent and MCP subsystems:- SSRF protection: MCP client now validates URLs before connecting, blocking private/internal IP addresses (RFC 1918, loopback, link-local, cloud metadata endpoints, IPv6 unique local and link-local ranges)
- OAuth log redaction: Removed OAuth state parameter value from
consumeStatewarning logs to prevent sensitive data leakage - Error sanitization: MCP server error strings are now sanitized (control characters stripped, truncated to 500 chars) before broadcasting to clients to mitigate XSS risk
sendIdentityOnConnectwarning: When using custom routing (where the instance name is not visible in the URL), a one-time console warning now informs developers that the instance name is being sent to clients. Setstatic options = { sendIdentityOnConnect: false }to opt out, ortrueto silence the warning.
-
#992
4fcf179Thanks @Muhammad-Bin-Ali! - Fix email routing to handle lowercased agent names from email infrastructureEmail servers normalize addresses to lowercase, so
SomeAgent+id@domain.comarrives assomeagent+id@domain.com. The router now registers a lowercase key in addition to the original binding name and kebab-case version, so all three forms resolve correctly.
-
#565
0e9a607Thanks @mattzcarey! - Add RPC transport for MCP: connect an Agent to an McpAgent via Durable Object bindingsNew feature:
addMcpServerwith DO bindingAgents can now connect to McpAgent instances in the same Worker using RPC transport — no HTTP, no network overhead. Pass the Durable Object namespace directly:
// In your Agent await this.addMcpServer("counter", env.MY_MCP); // With props await this.addMcpServer("counter", env.MY_MCP, { props: { userId: "user-123", role: "admin" }, });
The
addMcpServermethod now acceptsstring | DurableObjectNamespaceas the second parameter with proper TypeScript overloads, so HTTP and RPC paths are type-safe and cannot be mixed.Hibernation support
RPC connections survive Durable Object hibernation automatically. The binding name and props are persisted to storage and restored on wake-up, matching the behavior of HTTP MCP connections. No need to manually re-establish connections in
onStart().Deduplication
Calling
addMcpServerwith the same server name multiple times (e.g., across hibernation cycles) now returns the existing connection instead of creating duplicates. This applies to both RPC and HTTP connections. Connection IDs are stable across hibernation restore.Other changes
- Rewrote
RPCClientTransportto accept aDurableObjectNamespaceand create the stub internally viagetServerByNamefrom partyserver, instead of requiring a pre-constructed stub - Rewrote
RPCServerTransportto drop session management (unnecessary for DO-scoped RPC) and useJSONRPCMessageSchemafrom the MCP SDK for validation instead of 170 lines of hand-written validation - Removed
_resolveRpcBinding,_buildRpcTransportOptions,_buildHttpTransportOptions, and_connectToMcpServerInternalfrom the Agent base class — RPC transport logic no longer leaks intoindex.ts - Added
AddRpcMcpServerOptionstype (discriminated fromAddMcpServerOptions) sopropsis only available when passing a binding - Added
RPC_DO_PREFIXconstant used consistently across all RPC naming - Fixed
MCPClientManager.callToolpassingserverIdthrough toconn.client.callTool(it should be stripped before the call) - Added
getRpcServersFromStorage()andsaveRpcServerToStorage()toMCPClientManagerfor hibernation persistence restoreConnectionsFromStoragenow skips RPC servers (restored separately by the Agent class which has access toenv)- Reduced
rpc.tsfrom 609 lines to 245 lines - Reduced
types.tsfrom 108 lines to 26 lines - Updated
mcp-rpc-transportexample to use Workers AI (no API keys needed), Kumo/agents-ui components, and Tailwind CSS - Updated MCP transports documentation
- Rewrote
-
#973
969fbffThanks @threepointone! - Update dependencies -
#960
179b8cbThanks @mattzcarey! - Harden JSON Schema to TypeScript converter for production use- Add depth and circular reference guards to prevent stack overflows on recursive or deeply nested schemas
- Add
$refresolution for internal JSON Pointers (#/definitions/...,#/$defs/...,#) - Add tuple support (
prefixItemsfor JSON Schema 2020-12, arrayitemsfor draft-07) - Add OpenAPI 3.0
nullable: truesupport across all schema branches - Fix string escaping in enum/const values, property names (control chars, U+2028/U+2029), and JSDoc comments (
*/) - Add per-tool error isolation in
generateTypes()so one malformed schema cannot crash the pipeline - Guard missing
inputSchemaingetAITools()with a fallback to{ type: "object" } - Add per-tool error isolation in
getAITools()so one bad MCP tool does not break the entire tool set
-
#963
b848008Thanks @threepointone! - MakecallbackHostoptional inaddMcpServerfor non-OAuth serversPreviously,
addMcpServer()always required acallbackHost(either explicitly or derived from the request context) and eagerly created an OAuth auth provider, even when connecting to MCP servers that do not use OAuth. This made simple non-OAuth connections unnecessarily difficult, especially from WebSocket callable methods where the request context origin is unreliable.Now,
callbackHostand the OAuth auth provider are only required when the MCP server actually needs OAuth (returns a 401/AUTHENTICATING state). For non-OAuth servers,addMcpServer("name", url)works with no additional options. If an OAuth server is encountered without acallbackHost, a clear error is thrown: "This MCP server requires OAuth authentication. Provide callbackHost in addMcpServer options to enable the OAuth flow."The restore-from-storage flow also handles missing callback URLs gracefully, skipping auth provider creation for non-OAuth servers.
-
97c6702Thanks @threepointone! - Add one-time console warning when using RPC transport (DO binding) withaddMcpServer, noting the API is experimental and linking to the feedback issue.
-
#954
943c407Thanks @threepointone! - update dependencies -
#944
e729b5dThanks @threepointone! - ExportDurableObjectOAuthClientProviderfrom top-levelagentspackage and fixrestoreConnectionsFromStorage()to use the Agent'screateMcpOAuthProvider()override instead of hardcoding the default provider -
#850
2cb12dfThanks @Muhammad-Bin-Ali! - Fix: MCP OAuth callback errors are now returned as structured results instead of throwing unhandled exceptions. Errors with an active connection properly transition to "failed" state and are surfaced to clients via WebSocket broadcast.
This release adds per-connection protocol message control and a built-in retry system. Agents can now suppress JSON protocol frames for binary-only clients (MQTT, IoT devices) while keeping RPC and regular messaging working — useful for Durable Objects that serve mixed connection types. The new this.retry() method and per-task retry options bring exponential backoff with jitter to scheduling, queues, and MCP connections without external dependencies. This release also improves scheduling ergonomics with synchronous getter methods, a cleaner discriminated union schema, and fixes for hibernation, deep type recursion, and SSE keepalives.
-
#920
4dea3bdThanks @threepointone! - AddshouldSendProtocolMessageshook andisConnectionProtocolEnabledpredicate for per-connection control of protocol text framesAdds the ability to suppress protocol messages (
CF_AGENT_IDENTITY,CF_AGENT_STATE,CF_AGENT_MCP_SERVERS) on a per-connection basis. This is useful for binary-only clients (e.g. MQTT devices) that cannot handle JSON text frames.Override
shouldSendProtocolMessages(connection, ctx)to returnfalsefor connections that should not receive protocol messages. These connections still fully participate in RPC and regular messaging — only the automatic protocol text frames are suppressed, both on connect and during broadcasts.Use
isConnectionProtocolEnabled(connection)to check a connection's protocol status at any time.Also fixes
isConnectionReadonlyto correctly survive Durable Object hibernation by re-wrapping the connection when the in-memory accessor cache has been cleared. -
#874
a6ec9b0Thanks @threepointone! - Add retry utilities:this.retry(), per-task retry options, andRetryOptionstypethis.retry(fn, options?)— retry any async operation with exponential backoff and jitter. Accepts optionalshouldRetrypredicate to bail early on non-retryable errors.queue(),schedule(),scheduleEvery()accept{ retry?: RetryOptions }for per-task retry configuration, persisted in SQLite alongside the task.addMcpServer()accepts{ retry?: RetryOptions }for configurable MCP connection retries.RetryOptionstype is exported for TypeScript consumers.- Retry options are validated eagerly at enqueue/schedule time — invalid values throw immediately.
- Class-level retry defaults via
static options = { retry: { ... } }— override defaults for an entire agent class. - Internal retries added for workflow operations (
terminateWorkflow,pauseWorkflow, etc.) with Durable Object-aware error detection.
-
#899
04c6411Thanks @threepointone! - Fix React hooks exhaustive-deps warning in useAgent by referencing cacheInvalidatedAt inside useMemo body. -
#904
d611b94Thanks @ask-bonk! - Fix TypeScript "excessively deep" error with deeply nested state typesAdd a depth counter to
CanSerializeandIsSerializableParamtypes that bails out totrueafter 10 levels of recursion. This prevents the "Type instantiation is excessively deep and possibly infinite" error when using deeply nested types like AI SDKCoreMessage[]as agent state. -
#911
67b1601Thanks @threepointone! - Update all dependencies and fix breaking changes.Update all dependencies, add required
aria-labelprops to KumoButtoncomponents withshape(now required for accessibility), and fix state test for constructor-time validation of conflictingonStateChanged/onStateUpdatehooks. -
#889
9100e65Thanks @deathbyknowledge! - Fix scheduling schema compatibility with zod v3 and improve schema structure.- Change
zod/v3import tozodso the package works for users on zod v3 (who don't have thezod/v3subpath). - Replace flat object with optional fields with a
z.discriminatedUniononwhen.type. Each scheduling variant now only contains the fields it needs, making the schema cleaner and easier for LLMs to follow. - Replace
z.coerce.date()withz.string(). Zod v4'stoJSONSchema()cannot representDate, and the AI SDK routes zod v4 schemas through it directly. Dates are now returned as ISO 8601 strings. - Type change:
Schedule["when"]is now a discriminated union instead of a flat object with optional fields.when.dateisstringinstead ofDate.
- Change
-
#916
24e16e0Thanks @threepointone! - Widen peer dependency ranges across packages to prevent cascading major bumps during 0.x minor releases. Mark@cloudflare/ai-chatand@cloudflare/codemodeas optional peer dependencies ofagentsto fix unmet peer dependency warnings during installation. -
#898
cd2d34fThanks @jvg123! - Add keepalive ping to POST SSE response streams in WorkerTransportThe GET SSE handler already sends
event: pingevery 30 seconds to keep the connection alive, but the POST SSE handler did not. This caused POST response streams to be silently dropped by proxies and infrastructure during long-running tool calls (e.g., MCP tools/call), resulting in clients never receiving the response. -
#874
a6ec9b0Thanks @threepointone! - Make queue and schedule getter methods synchronousgetQueue(),getQueues(),getSchedule(),dequeue(),dequeueAll(), anddequeueAllByCallback()were unnecessarilyasyncdespite only performing synchronous SQL operations. They now return values directly instead of wrapping them in Promises. This is backward compatible — existing code usingawaiton these methods will continue to work.
-
#890
22dbd2cThanks @ask-bonk! - Fix_flushQueue()permanently blocking when a queued callback throwsA throwing callback in
_flushQueue()previously caused the failing row to never be dequeued, creating an infinite retry loop that blocked all subsequent queued tasks. Additionally,_flushingQueuewas never reset tofalseon error, permanently locking the queue for the lifetime of the Durable Object instance.The fix wraps each callback invocation in try-catch-finally so that failing items are always dequeued and subsequent items continue processing. The
_flushingQueueflag is now reset in a top-level finally block. Missing callbacks are also dequeued instead of being skipped indefinitely.Note for existing stuck Durable Objects: This fix is self-healing for poison rows — they will be properly dequeued on the next
_flushQueue()call. However,_flushQueue()is only triggered by a newqueue()call, not on DO initialization. If you have DOs stuck in production, you can either trigger a newqueue()call on affected DOs, or calldequeueAll()/dequeueAllByCallback()to clear the poison rows manually. A future improvement may add a_flushQueue()call toonStart()so stuck DOs self-heal on wake. -
#891
0723b99Thanks @ask-bonk! - FixgetCurrentAgent()returningundefinedconnection when used with@cloudflare/ai-chatand Vite SSRRe-export
agentContextas__DO_NOT_USE_WILL_BREAK__agentContextfrom the mainagentsentry point and update@cloudflare/ai-chatto import it fromagentsinstead of theagents/internal_contextsubpath export. This prevents Vite SSR pre-bundling from creating two separateAsyncLocalStorageinstances, which causedgetCurrentAgent().connectionto beundefinedinsideonChatMessageand toolexecutefunctions.The
agents/internal_contextsubpath export has been removed frompackage.jsonand the deprecatedagentContextalias has been removed frominternal_context.ts. This was never a public API. -
Updated dependencies [
584cebe,0723b99,4292f6b]:- @cloudflare/ai-chat@0.0.8
-
#848
a167344Thanks @mattzcarey! - Upgrade MCP SDK to 1.26.0 to prevent cross-client response leakage. Updated examples for stateless MCP Servers create newMcpServerinstance per request instead of sharing a single instance. A guard is added in this version of the MCP SDK which will prevent connection to a Server instance that has already been connected to a transport. Developers will need to modify their code if they declare theirMcpServerinstance as a global variable. -
#298
27f4e3eThanks @jaredhanson! - AddcreateMcpOAuthProvidermethod to theAgentclass, allowing subclasses to override the default OAuth provider used when connecting to MCP servers. This enables custom authentication strategies such as pre-registered client credentials or mTLS, beyond the built-in dynamic client registration. -
#610
f59f305Thanks @threepointone! - DeprecateonStateUpdateserver-side hook in favor ofonStateChangedonStateChangedis a drop-in rename ofonStateUpdate(same signature, same behavior)onStateUpdatestill works but emits a one-time console warning per class- Throws if a class overrides both hooks simultaneously
validateStateChangerejections now propagate aCF_AGENT_STATE_ERRORmessage back to the client
-
#871
27f8f75Thanks @threepointone! - Migrate x402 MCP integration from legacyx402package to@x402/coreand@x402/evmv2Breaking changes for x402 users:
- Peer dependencies changed: replace
x402with@x402/coreand@x402/evm PaymentRequirementstype now uses v2 fields (e.g.amountinstead ofmaxAmountRequired)X402ClientConfig.accounttype changed fromviem.AccounttoClientEvmSigner(structurally compatible withprivateKeyToAccount())
Migration guide:
-
Update dependencies:
npm uninstall x402 npm install @x402/core @x402/evm
-
Update network identifiers — both legacy names and CAIP-2 format are accepted:
// Before { network: "base-sepolia"; } // After (either works) { network: "base-sepolia"; } // legacy name, auto-converted { network: "eip155:84532"; } // CAIP-2 format (preferred)
-
If you access
PaymentRequirementsfields in callbacks, update to v2 field names (see@x402/coredocs). -
The
versionfield onX402ConfigandX402ClientConfigis now deprecated and ignored — the protocol version is determined automatically.
Other changes:
X402ClientConfig.networkis now optional — the client auto-selects from available payment requirements- Server-side lazy initialization: facilitator connection is deferred until the first paid tool invocation
- Payment tokens support both v2 (
PAYMENT-SIGNATURE) and v1 (X-PAYMENT) HTTP headers - Added
normalizeNetworkexport for converting legacy network names to CAIP-2 format - Re-exports
PaymentRequirements,PaymentRequired,Network,FacilitatorConfig, andClientEvmSignerfromagents/x402
- Peer dependencies changed: replace
-
#610
f59f305Thanks @threepointone! - Add readonly connections: restrict WebSocket clients from modifying agent state- New hooks:
shouldConnectionBeReadonly,setConnectionReadonly,isConnectionReadonly - Blocks both client-side
setState()and mutating@callable()methods for readonly connections - Readonly flag stored in a namespaced connection attachment (
_cf_readonly), surviving hibernation without extra SQL - Connection state wrapping hides the internal flag from user code and preserves it across
connection.setState()calls - Client-side
onStateUpdateErrorcallback for handling rejected state updates
- New hooks:
-
#855
271a3cfThanks @threepointone! - FixuseAgentandAgentClientcrashing when usingbasePathrouting. -
#868
b3e2dc1Thanks @threepointone! - Fix MCP OAuth callback URL leaking instance nameAdd
callbackPathoption toaddMcpServerto prevent instance name leakage in MCP OAuth callback URLs. WhensendIdentityOnConnectisfalse,callbackPathis now required — the default callback URL would expose the instance name, undermining the security intent. Also fixes callback request detection to match via thestateparameter instead of a loose/callbackURL substring check, enabling custom callback paths. -
#872
de71f9eThanks @threepointone! - update dependencies -
8893fbeThanks @threepointone! - partykit releases- #319 — Add
configurable: trueto thestate,setState,serializeAttachment, anddeserializeAttachmentproperty descriptors on connection objects. This allows downstream consumers (like the Cloudflare Agents SDK) to redefine these properties withObject.definePropertyfor namespacing or wrapping internal state storage. Default behavior is unchanged.
- #320 — Add CORS support to
routePartykitRequest. Passcors: truefor permissive defaults orcors: { ...headers }for custom CORS headers. Preflight (OPTIONS) requests are handled automatically for matched routes, and CORS headers are appended to all non-WebSocket responses — including responses returned byonBeforeRequest. - #260 — Remove redundant initialize code as
setNametakes care of it, along with the nestedblockConcurrencyWhilecall.
- #317 — Fix
PartySocket.reconnect()crashing when usingbasePathwithoutroom. The reconnect guard now accepts eitherroomorbasePathas sufficient context to construct a connection URL. - #319 — Throw a clear error when constructing a
PartySocketwithoutroomorbasePath(and withoutstartClosed: true), instead of silently connecting to a malformed URL containing"undefined"as the room name.
- #322 — Fix
reconnect()not working aftermaxRetrieshas been exhausted. The_connectLockwas not released when the max retries early return was hit in_connect(), preventing any subsequentreconnect()call from initiating a new connection.
- #319 — Add
-
#869
fc17506Thanks @threepointone! - Removeroom/partyworkaround forbasePathrouting now that partysocket handles reconnect without requiringroomto be set. -
#873
d0579faThanks @threepointone! - Remove CORS wrapping fromrouteAgentRequestand delegate to partyserver's native CORS support. Thecorsoption is now passed directly through toroutePartykitRequest, which handles preflight and response headers automatically since partyserver 0.1.4. -
#865
c3211d0Thanks @threepointone! - update dependencies -
Updated dependencies [
21a7977,3de98a3,c3211d0]:- @cloudflare/codemode@0.0.7
- @cloudflare/ai-chat@0.0.7
-
#839
68916bfThanks @whoiskatrin! - Invalidate query cache on disconnect to fix stale auth tokens -
#841
3f490d0Thanks @mattzcarey! - Escape authError to prevent XSS attacks and store it in the connection state to avoid needing script tags to display error. -
Updated dependencies [
83f137f]:- @cloudflare/ai-chat@0.0.6
-
#837
b11b9ddThanks @threepointone! - Fix AgentWorkflow run() method not being called in productionThe
run()method wrapper was being set as an instance property in the constructor, but Cloudflare's RPC system invokes methods from the prototype chain. This caused the initialization wrapper to be bypassed in production, resulting in_initAgentnever being called.Changed to wrap the subclass prototype's
runmethod directly with proper safeguards:- Uses
Object.hasOwn()to only wrap prototypes that define their ownrunmethod (prevents double-wrapping inherited methods) - Uses a
WeakSetto track wrapped prototypes (prevents re-wrapping on subsequent instantiations) - Uses an instance-level
__agentInitCalledflag to prevent double initialization ifsuper.run()is called from a subclass
- Uses
-
#833
6c80022Thanks @tarushnagpal! - On invalid OAuth state, clear auth_url in storage and set the MCP connection state to FAILED ready for reconnection. -
#834
2b4aecdThanks @threepointone! - Fix AgentClient.close() to immediately reject pending RPC calls instead of waiting for WebSocket close handshake timeout.Previously, calling
client.close()would not reject pending RPC calls until the WebSocket close handshake completed (which could take 15+ seconds in some environments). Now pending calls are rejected immediately whenclose()is called, providing faster feedback on intentional disconnects.
This release introduces Cloudflare Workflows integration for durable multi-step processing, secure email reply routing with HMAC-SHA256 signatures, 15+ new documentation files, and significant improvements to state management, the callable RPC system, and scheduling.
- Workflows Integration - Seamless integration between Cloudflare Agents and Cloudflare Workflows for durable, multi-step background processing
- Secure Email Routing - HMAC-SHA256 signed email headers prevent unauthorized routing of emails to agent instances
- Comprehensive Documentation - 15+ new docs covering getting started, state, routing, HTTP/WebSocket lifecycle, callable methods, MCP, and scheduling
- Synchronous
setState()- State updates are now synchronous with a newvalidateStateChange()validation hook scheduleEvery()Method - Fixed-interval recurring tasks with overlap prevention- Callable System Improvements - Client-side RPC timeouts, streaming error signaling, introspection API
- 100+ New Tests - Comprehensive test coverage across state, routing, callable, and email utilities
Agents excel at real-time communication and state management. Workflows excel at durable execution. Together, they enable powerful patterns where Agents handle WebSocket connections while Workflows handle long-running tasks, retries, and human-in-the-loop flows.
Extend AgentWorkflow instead of WorkflowEntrypoint to get typed access to the originating Agent:
import { AgentWorkflow } from "agents/workflows";
export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
// Call Agent methods via RPC
await this.agent.updateStatus(params.taskId, "processing");
// Non-durable: progress reporting
await this.reportProgress({ step: "process", percent: 0.5 });
this.broadcastToClients({ type: "update", taskId: params.taskId });
// Durable via step: idempotent, won't repeat on retry
await step.mergeAgentState({ taskProgress: 0.5 });
await step.reportComplete(result);
return result;
}
}runWorkflow(workflowName, params, options?)- Start workflow with optional metadatasendWorkflowEvent(workflowName, workflowId, event)- Send events to waiting workflowsgetWorkflow(workflowId)/getWorkflows(criteria?)- Query workflows with cursor-based paginationdeleteWorkflow(workflowId)/deleteWorkflows(criteria?)- Delete workflows by ID or criteriaapproveWorkflow(workflowId)/rejectWorkflow(workflowId)- Human-in-the-loop approval flowsterminateWorkflow(),pauseWorkflow(),resumeWorkflow(),restartWorkflow()- Workflow control
async onWorkflowProgress(workflowName, workflowId, progress) {}
async onWorkflowComplete(workflowName, workflowId, result?) {}
async onWorkflowError(workflowName, workflowId, error) {}
async onWorkflowEvent(workflowName, workflowId, event) {}See docs/workflows.md for full documentation.
Prevents unauthorized routing of emails to arbitrary agent instances using HMAC-SHA256 signed headers.
import { createSecureReplyEmailResolver } from "agents/email";
const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
maxAge: 7 * 24 * 60 * 60, // Optional: 7 days (default: 30 days)
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
},
});await this.replyToEmail(email, {
fromName: "My Agent",
body: "Thanks!",
secret: this.env.EMAIL_SECRET, // Signs headers for secure reply routing
});- Email utilities moved to
agents/emailsubpath createHeaderBasedEmailResolverremoved (security vulnerability)- New
onNoRoutecallback for handling unmatched emails
| Document | Description |
|---|---|
getting-started.md |
Quick start guide: installation, first agent, state basics, deployment |
adding-to-existing-project.md |
Integrating agents into existing Workers, React apps, Hono |
state.md |
State management, validateStateChange(), persistence, client sync |
routing.md |
URL routing patterns, basePath, server-sent identity |
http-websockets.md |
HTTP/WebSocket lifecycle hooks, connection management, hibernation |
callable-methods.md |
@callable decorator, RPC over WebSocket, streaming responses |
mcp-client.md |
Connecting to MCP servers, OAuth flows, transport options |
scheduling.md |
One-time, recurring (scheduleEvery), and cron-based scheduling |
workflows.md |
Complete Workflows integration guide |
setState() is now synchronous. Existing await this.setState(...) code continues to work.
// Preferred (new)
this.setState({ count: 1 });
// Still works (backward compatible)
await this.setState({ count: 1 });New synchronous validation hook that runs before state is persisted:
validateStateChange(nextState: State, source: Connection | "server") {
if (nextState.count < 0) {
throw new Error("Count cannot be negative");
}
}validateStateChange(nextState, source)- validation (sync, gating)- State persisted to SQLite
- State broadcast to connected clients
onStateUpdate(nextState, source)- notifications (async viactx.waitUntil, non-gating)
Fixed-interval recurring tasks with overlap prevention and error resilience:
await this.scheduleEvery(60, "cleanup");
await this.scheduleEvery(300, "syncData", { source: "api" });- Validates interval doesn't exceed 30 days (DO alarm limit)
- Overlap prevention with hung callback detection (configurable via
hungScheduleTimeoutSeconds)
await agent.call("method", [args], {
timeout: 5000,
stream: { onChunk, onDone, onError },
});StreamingResponse.error(message)- Graceful stream error signalinggetCallableMethods()- Introspection API for callable methods- Connection close handling - Pending calls rejected on disconnect
crypto.randomUUID()for more robust RPC IDs- Streaming observability events and error logging
Options-based addMcpServer() overload for cleaner configuration:
await this.addMcpServer("server", url, {
callbackHost: "https://my-worker.workers.dev",
transport: { headers: { Authorization: "Bearer ..." } },
});basePath- Bypass default URL construction for custom routing- Server-sent identity - Agents send
nameandagenttype on connect onIdentity/onIdentityChangecallbacks on the clientstatic options = { sendIdentityOnConnect }for server-side control
const agent = useAgent({
basePath: "user",
onIdentity: (name, agentType) => console.log(`Connected to ${name}`),
});isAutoReplyEmail(headers)- Detect auto-reply emails using standard RFC headers
- Fixed tool error content type in
getAITools(#781) - Fixed React
useReftype error - Memory leak prevention with WeakMap for callable metadata
- Connection cleanup - pending RPC calls rejected on WebSocket close
- JSON parse error handling - graceful fallback to
initialStateon corrupted state - Fixed resumable streaming to avoid delivering live chunks before resume ACK (#795)
// Before
import { createAddressBasedEmailResolver, signAgentHeaders } from "agents";
// After
import {
createAddressBasedEmailResolver,
signAgentHeaders,
} from "agents/email";import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowStep, WorkflowInfo } from "agents/workflows";When using scheduleSchema with OpenAI models via the AI SDK, pass providerOptions:
await generateObject({
// ... other options
providerOptions: { openai: { strictJsonSchema: false } },
});-
#825
0c3c9bbThanks @threepointone! - Add cursor-based pagination togetWorkflows(). Returns aWorkflowPagewith workflows, total count, and cursor for next page. Default limit is 50 (max 100). -
#825
0c3c9bbThanks @threepointone! - Add workflow control methods:terminateWorkflow(),pauseWorkflow(),resumeWorkflow(), andrestartWorkflow(). -
#799
d1a0c2bThanks @threepointone! - feat: Add Cloudflare Workflows integration for AgentsAdds seamless integration between Cloudflare Agents and Cloudflare Workflows for durable, multi-step background processing.
Agents excel at real-time communication and state management, while Workflows excel at durable execution. Together:
- Agents handle WebSocket connections and quick operations
- Workflows handle long-running tasks, retries, and human-in-the-loop flows
Extend
AgentWorkflowinstead ofWorkflowEntrypointto get typed access to the originating Agent:export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> { async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) { const params = event.payload; // Call Agent methods via RPC await this.agent.updateStatus(params.taskId, "processing"); // Non-durable: progress reporting (lightweight, for frequent updates) await this.reportProgress({ step: "process", percent: 0.5, message: "Halfway done", }); this.broadcastToClients({ type: "update", taskId: params.taskId }); // Durable via step: idempotent, won't repeat on retry await step.mergeAgentState({ taskProgress: 0.5 }); await step.reportComplete(result); return result; } }
runWorkflow(workflowName, params, options?)- Start workflow with optional metadata for queryingsendWorkflowEvent(workflowName, workflowId, event)- Send events to waiting workflowsgetWorkflow(workflowId)- Get tracked workflow by IDgetWorkflows(criteria?)- Query by status, workflowName, or metadata with paginationdeleteWorkflow(workflowId)- Delete a workflow tracking recorddeleteWorkflows(criteria?)- Delete workflows by criteria (status, workflowName, metadata, createdBefore)approveWorkflow(workflowId, data?)- Approve a waiting workflowrejectWorkflow(workflowId, data?)- Reject a waiting workflow
On
this(non-durable, lightweight):reportProgress(progress)- Report typed progress object to AgentbroadcastToClients(message)- Broadcast to WebSocket clientswaitForApproval(step, opts?)- Wait for approval (throws on rejection)
On
step(durable, idempotent):step.reportComplete(result?)- Report successful completionstep.reportError(error)- Report an errorstep.sendEvent(event)- Send custom event to Agentstep.updateAgentState(state)- Replace Agent state (broadcasts to clients)step.mergeAgentState(partial)- Merge into Agent state (broadcasts to clients)step.resetAgentState()- Reset Agent state to initialState (broadcasts to clients)
Override these methods to handle workflow events (workflowName is first for easy differentiation):
async onWorkflowProgress(workflowName, workflowId, progress) {} // progress is typed object async onWorkflowComplete(workflowName, workflowId, result?) {} async onWorkflowError(workflowName, workflowId, error) {} async onWorkflowEvent(workflowName, workflowId, event) {}
Workflows are automatically tracked in
cf_agents_workflowsSQLite table:- Status, timestamps, errors
- Optional
metadatafield for queryable key-value data - Params/output NOT stored by default (could be large)
See
docs/workflows.mdfor full documentation. -
#812
6218541Thanks @threepointone! - # Bug FixesThis release includes three bug fixes:
Fixed a deadlock where if an interval callback hung indefinitely, all future interval executions would be skipped forever.
Fix: Track execution start time and force reset after 30 seconds of inactivity. If a previous execution appears hung (started more than 30s ago), it is force-reset and re-executed.
// Now safe - hung callbacks won't block future executions await this.scheduleEvery(60, "myCallback");
Fixed a crash when the database contains malformed JSON state.
Fix: Wrapped
JSON.parsein try-catch with fallback toinitialState. If parsing fails, the agent logs an error and recovers gracefully.// Agent now survives corrupted state class MyAgent extends Agent { initialState = { count: 0 }; // Used as fallback if DB state is corrupted }
Fixed
getCallableMethods()to find@callablemethods from parent classes, not just the immediate class.Fix: Walk the full prototype chain using
Object.getPrototypeOf()loop.class BaseAgent extends Agent { @callable() parentMethod() { return "parent"; } } class ChildAgent extends BaseAgent { @callable() childMethod() { return "child"; } } // Now correctly returns both parentMethod and childMethod const methods = childAgent.getCallableMethods();
-
#812
6218541Thanks @threepointone! - # Callable System ImprovementsThis release includes several improvements to the
@callabledecorator and RPC system:You can now specify a timeout for RPC calls that will reject if the call doesn't complete in time:
await agent.call("slowMethod", [], { timeout: 5000 });
New method to gracefully signal an error during streaming and close the stream:
@callable({ streaming: true }) async processItems(stream: StreamingResponse, items: string[]) { for (const item of items) { try { const result = await this.process(item); stream.send(result); } catch (e) { stream.error(`Failed to process ${item}: ${e.message}`); return; } } stream.end(); }
New method on the Agent class to introspect all callable methods and their metadata:
const methods = agent.getCallableMethods(); // Returns Map<string, CallableMetadata> for (const [name, meta] of methods) { console.log(`${name}: ${meta.description || "(no description)"}`); }
Pending RPC calls are now automatically rejected with a "Connection closed" error when the WebSocket connection closes unexpectedly.
- WeakMap for metadata storage: Changed
callableMetadatafromMaptoWeakMapto prevent memory leaks when function references are garbage collected. - UUID for RPC IDs: Replaced
Math.random().toString(36)withcrypto.randomUUID()for more robust and unique RPC call identifiers. - Streaming observability: Added observability events for streaming RPC calls.
The
agent.call()method now accepts a unifiedCallOptionsobject with timeout support:// New format (preferred, supports timeout) await agent.call("method", [args], { timeout: 5000, stream: { onChunk, onDone, onError }, }); // Legacy format (still fully supported for backward compatibility) await agent.call("method", [args], { onChunk, onDone, onError });
Both formats work seamlessly - the client auto-detects which format you're using.
- WeakMap for metadata storage: Changed
-
#812
6218541Thanks @threepointone! - feat: AddscheduleEverymethod for fixed-interval schedulingAdds a new
scheduleEvery(intervalSeconds, callback, payload?)method to the Agent class for scheduling recurring tasks at fixed intervals.- Fixed interval execution: Schedule a callback to run every N seconds
- Overlap prevention: If a callback is still running when the next interval fires, the next execution is skipped
- Error resilience: If a callback throws, the schedule persists and continues on the next interval
- Cancellable: Use
cancelSchedule(id)to stop the recurring schedule
class MyAgent extends Agent { async onStart() { // Run cleanup every 60 seconds await this.scheduleEvery(60, "cleanup"); // With payload await this.scheduleEvery(300, "syncData", { source: "api" }); } cleanup() { // Runs every 60 seconds } syncData(payload: { source: string }) { // Runs every 300 seconds with payload } }
// Get all interval schedules const intervals = await this.getSchedules({ type: "interval" });
Adds
intervalSecondsandrunningcolumns tocf_agents_schedulestable (auto-migrated for existing agents). -
#812
6218541Thanks @threepointone! - AddisAutoReplyEmail()utility to detect auto-reply emailsDetects auto-reply emails based on standard RFC 3834 headers (
Auto-Submitted,X-Auto-Response-Suppress,Precedence). Use this to avoid mail loops when sending automated replies.import { isAutoReplyEmail } from "agents/email"; import PostalMime from "postal-mime"; async onEmail(email: AgentEmail) { const raw = await email.getRaw(); const parsed = await PostalMime.parse(raw); // Detect and skip auto-reply emails if (isAutoReplyEmail(parsed.headers)) { console.log("Skipping auto-reply"); return; } // Process the email... }
-
#781
fd79481Thanks @HueCodes! - fix: properly type tool error content in getAITools -
#812
6218541Thanks @threepointone! - fix: improve type inference for RPC methods returning custom interfacesPreviously,
RPCMethodused{ [key: string]: SerializableValue }to check if return types were serializable. This didn't work with TypeScript interfaces that have named properties (likeinterface CoreState { counter: number; name: string; }), causing those methods to be incorrectly excluded from typed RPC calls.Now uses a recursive
CanSerialize<T>type that checks if all properties of an object are serializable, properly supporting:- Custom interfaces with named properties
- Nested object types
- Arrays of objects
- Optional and nullable properties
- Union types
Also expanded
NonSerializableto explicitly exclude non-JSON-serializable types likeDate,RegExp,Map,Set,Error, and typed arrays.// Before: these methods were NOT recognized as callable interface MyState { counter: number; items: string[]; } class MyAgent extends Agent<Env, MyState> { @callable() getState(): MyState { return this.state; } // ❌ Not typed } // After: properly recognized and typed const agent = useAgent<MyAgent, MyState>({ agent: "my-agent" }); agent.call("getState"); // ✅ Typed as Promise<MyState>
-
#825
0c3c9bbThanks @threepointone! - Fix workflow tracking table not being updated by AgentWorkflow callbacks.Previously, when a workflow reported progress, completion, or errors via callbacks, the
cf_agents_workflowstracking table was not updated. This causedgetWorkflow()andgetWorkflows()to return stale status (e.g., "queued" instead of "running" or "complete").Now,
onWorkflowCallback()automatically updates the tracking table:- Progress callbacks set status to "running"
- Complete callbacks set status to "complete" with
completed_attimestamp - Error callbacks set status to "errored" with error details
Fixes #821.
-
#812
6218541Thanks @threepointone! - feat: Add options-based API foraddMcpServerAdds a cleaner options-based overload for
addMcpServer()that avoids passingundefinedfor unused positional parameters.// Awkward when you only need transport options await this.addMcpServer("server", url, undefined, undefined, { transport: { headers: { Authorization: "Bearer ..." } }, });
// Clean options object await this.addMcpServer("server", url, { transport: { headers: { Authorization: "Bearer ..." } }, }); // With callback host await this.addMcpServer("server", url, { callbackHost: "https://my-worker.workers.dev", transport: { type: "sse" }, });
type AddMcpServerOptions = { callbackHost?: string; // OAuth callback host (auto-derived if omitted) agentsPrefix?: string; // Routing prefix (default: "agents") client?: ClientOptions; // MCP client options transport?: { headers?: HeadersInit; // Custom headers for auth type?: "sse" | "streamable-http" | "auto"; }; };
The legacy 5-parameter signature remains fully supported for backward compatibility.
-
#812
6218541Thanks @threepointone! - Add custom URL routing withbasePathand server-sent identityNew
basePathoption bypasses default/agents/{agent}/{name}URL construction, enabling custom routing patterns:// Client connects to /user instead of /agents/user-agent/... const agent = useAgent({ agent: "UserAgent", basePath: "user", });
Server handles routing manually with
getAgentByName:export default { async fetch(request: Request, env: Env) { const url = new URL(request.url); if (url.pathname === "/user") { const session = await getSession(request); const agent = await getAgentByName(env.UserAgent, session.userId); return agent.fetch(request); } return ( (await routeAgentRequest(request, env)) ?? new Response("Not found", { status: 404 }) ); }, };
Agents now send their identity (
nameandagentclass) to clients on connect:onIdentitycallback - called when server sends identityagent.nameandagent.agentare updated from server (authoritative)
const agent = useAgent({ agent: "UserAgent", basePath: "user", onIdentity: (name, agentType) => { console.log(`Connected to ${agentType} instance: ${name}`); }, });
identified: boolean- whether identity has been receivedready: Promise<void>- resolves when identity is received- In React,
name,agent, andidentifiedare reactive state
// React - reactive rendering return agent.identified ? `Connected to: ${agent.name}` : "Connecting..."; // Vanilla JS - await ready await agent.ready; console.log(agent.name);
onIdentityChangecallback - fires when identity differs on reconnect- Warns if identity changes without handler (helps catch session issues)
useAgent({ basePath: "user", onIdentityChange: (oldName, newName, oldAgent, newAgent) => { console.log(`Session changed: ${oldName} → ${newName}`); }, });
Append additional path segments:
// /user/settings useAgent({ basePath: "user", path: "settings" }); // /agents/my-agent/room/settings useAgent({ agent: "MyAgent", name: "room", path: "settings" });
Disable identity sending for security-sensitive instance names:
class SecureAgent extends Agent { static options = { sendIdentityOnConnect: false }; }
-
#827
e20da53Thanks @threepointone! - Move workflow exports toagents/workflowssubpath for better separation of concerns.import { AgentWorkflow } from "agents/workflows"; import type { AgentWorkflowStep, WorkflowInfo } from "agents/workflows";
-
#811
f604008Thanks @threepointone! - ### Secure Email Reply RoutingThis release introduces secure email reply routing with HMAC-SHA256 signed headers, preventing unauthorized routing of emails to arbitrary agent instances.
Email utilities moved to
agents/emailsubpath: Email-specific resolvers and utilities have been moved to a dedicated subpath for better organization.// Before import { createAddressBasedEmailResolver, signAgentHeaders } from "agents"; // After import { createAddressBasedEmailResolver, signAgentHeaders, } from "agents/email";
The following remain in root:
routeAgentEmail,createHeaderBasedEmailResolver(deprecated).createHeaderBasedEmailResolverremoved: This function now throws an error with migration guidance. It was removed because it trusted attacker-controlled email headers for routing.Migration:
- For inbound mail: use
createAddressBasedEmailResolver(agentName) - For reply flows: use
createSecureReplyEmailResolver(secret)with signed headers
See https://github.com/cloudflare/agents/blob/main/docs/email.md for details.
EmailSendOptionstype removed: This type was unused and has been removed.createSecureReplyEmailResolver: A new resolver that verifies HMAC-SHA256 signatures on incoming emails before routing. Signatures include a timestamp and expire after 30 days by default.const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, { maxAge: 7 * 24 * 60 * 60, // Optional: 7 days (default: 30 days) onInvalidSignature: (email, reason) => { // Optional: log failures for debugging // reason: "missing_headers" | "expired" | "invalid" | "malformed_timestamp" console.warn(`Invalid signature from ${email.from}: ${reason}`); }, });
signAgentHeaders: Helper function to manually sign agent routing headers for use with external email services.const headers = await signAgentHeaders(secret, agentName, agentId); // Returns: { "X-Agent-Name", "X-Agent-ID", "X-Agent-Sig", "X-Agent-Sig-Ts" }
replyToEmailsigning: ThereplyToEmailmethod now accepts asecretoption to automatically sign outbound email headers.await this.replyToEmail(email, { fromName: "My Agent", body: "Thanks!", secret: this.env.EMAIL_SECRET, // Signs headers for secure reply routing });
If an email was routed via
createSecureReplyEmailResolver, callingreplyToEmailwithout a secret will throw an error (pass explicitnullto opt-out).onNoRoutecallback:routeAgentEmailnow accepts anonNoRoutecallback for handling emails that don't match any routing rule.await routeAgentEmail(message, env, { resolver, onNoRoute: (email) => { email.setReject("Unknown recipient"); }, });
- For inbound mail: use
-
#813
7aebab3Thanks @threepointone! - update dependencies -
#800
a54edf5Thanks @threepointone! - Update dependencies -
#818
7c74336Thanks @threepointone! - update dependencies -
#812
6218541Thanks @threepointone! - # SynchronoussetStatewith validation hooksetState()is now synchronous instead of async. This improves ergonomics and aligns with the expected mental model for state updates.// Before (still works - awaiting a non-promise is harmless) await this.setState({ count: 1 }); // After (preferred) this.setState({ count: 1 });
Existing code that uses
await this.setState(...)will continue to work without changes.Previously, if
onStateUpdate()threw an error, the state update would be aborted. Now,onStateUpdate()runs asynchronously viactx.waitUntil()after the state is persisted and broadcast. Errors inonStateUpdate()are routed toonError()but do not prevent the state from being saved or broadcast.If you were using
onStateUpdate()for validation, migrate tovalidateStateChange().A new synchronous hook that runs before state is persisted or broadcast. Use this for validation:
validateStateChange(nextState: State, source: Connection | "server") { if (nextState.count < 0) { throw new Error("Count cannot be negative"); } }
- Runs synchronously before persistence and broadcast
- Throwing aborts the state update entirely
- Ideal for validation logic
validateStateChange(nextState, source)- validation (sync, gating)- State persisted to SQLite
- State broadcast to connected clients
onStateUpdate(nextState, source)- notifications (async viactx.waitUntil, non-gating)
-
#815
ded8d3eThanks @threepointone! - docs: add OpenAI provider options documentation to scheduleSchemaWhen using
scheduleSchemawith OpenAI models via the AI SDK, users must now passproviderOptions: { openai: { strictJsonSchema: false } }togenerateObject. This is documented in the JSDoc forscheduleSchema.This is required because
@ai-sdk/openainow defaultsstrictJsonSchematotrue, which requires all schema properties to be in therequiredarray. ThescheduleSchemauses optional fields which are not compatible with this strict mode. -
Updated dependencies [
7aebab3,77be4f8,a54edf5,7c74336,99cbca0]:- @cloudflare/codemode@0.0.6
- @cloudflare/ai-chat@0.0.5
-
#786
395f461Thanks @deathbyknowledge! - fix: allow callable methods to return this.state -
#783
f27e62cThanks @Muhammad-Bin-Ali! - fix saving initialize params for stateless MCP server (effects eliciations and other optional features) -
Updated dependencies [
93c613e]:- @cloudflare/codemode@0.0.5
- #752
473e53cThanks @mattzcarey! - bump mcp sdk version to 1.25.2. changes error handling for not found see: https://github.com/cloudflare/agents/pull/752/changes#diff-176ef2d2154e76a8eb7862efb323210f8f1b434f6a9ff3f06abc87d8616855c9R25-R31
-
#768
cf8a1e7Thanks @whoiskatrin! - pipe SQL errors into the existing onError method using a new SqlError class -
#771
87dc96dThanks @threepointone! - update dependencies -
Updated dependencies [
0e8fc1e,87dc96d]:- @cloudflare/ai-chat@0.0.4
- @cloudflare/codemode@0.0.4
-
a5d0137Thanks @threepointone! - trigger a new release -
Updated dependencies [
a5d0137]:- @cloudflare/codemode@0.0.3
- @cloudflare/ai-chat@0.0.3
-
#756
0c4275fThanks @threepointone! - feat: split ai-chat and codemode into separate packagesExtract @cloudflare/ai-chat and @cloudflare/codemode into their own packages with comprehensive READMEs. Update agents README to remove chat-specific content and point to new packages. Fix documentation imports to reflect new package structure.
Maintains backward compatibility, no breaking changes.
-
#758
f12553fThanks @whoiskatrin! - Implement createStubProxy function to fix RPC method call handling -
Updated dependencies [
0c4275f]:- @cloudflare/codemode@0.0.2
- @cloudflare/ai-chat@0.0.2
- #754
e21051dThanks @threepointone! - fix: don't mark ai as optional under peerDependenciesMeta
-
accdd78Thanks @threepointone! - update to ai sdk v6via @whoiskatrin in #733
-
#742
29938d4Thanks @threepointone! - mark AgentNamespace as deprecatedIt only makes things harder, especially for autogenned types.
-
#747
17a0346Thanks @threepointone! - fix: scheduling should worksince we updated to zod v4, the schedule schema was broken. ai sdk's .jsonSchema function doesn't correctly work on tools created with zod v4. The fix, is to use the v3 version of zod for the schedule schema.
-
#739
e9b6bb7Thanks @threepointone! - update all dependencies- remove the changesets cli patch, as well as updating node version, so we don't need to explicitly install newest npm
- lock mcp sdk version till we figure out how to do breaking changes correctly
- removes stray permissions block from release.yml
-
#740
087264cThanks @threepointone! - update zod -
#737
b8c0595Thanks @threepointone! - update partyserver (and some other cf packages)specifically updating partyserver so it gets a better default type for Env, defaulting to Cloudflare.Env
-
#732
9fbb1b6Thanks @Scalahansolo! - Setup proper peer deps for zod v4 -
#722
57b7f2eThanks @agcty! - fix: move AI SDK packages to peer dependencies
-
#729
79843bdThanks @whoiskatrin! - add client-defined tools and prepareSendMessagesRequest options -
#726
59ac254Thanks @whoiskatrin! - fix cache ttl
- #720
380c597Thanks @mattzcarey! - MCP WorkerTransport accepts any supported protocol version in request headers and only rejects truly unsupported versions. This aligns with the move by MCP community to stateless transports and fixes an isse with 'mcp-protocol-version': '2025-11-25'
- #716
569e184Thanks @whoiskatrin! - Fix elicitation response handling in MCP StreamableHTTP transport by adding a message interceptor
-
#712
cd8b7fdThanks @whoiskatrin! - fix connection inside tool execution -
#710
d08612fThanks @whoiskatrin! - fix cachetll + test
-
#696
6a930efThanks @mattzcarey! - Enables connecting to multiple MCP servers simultaneously and hardens OAuth state handling against replay/DoS attacks.Note: Inflight OAuth flows that were initiated on a previous version will not complete after upgrading, as the state parameter format has changed. Users will need to restart the authentication flow.
-
#702
10d453dThanks @mattzcarey! - broadcast auth_url as soon as its returned
- #691
d7b2f14Thanks @whoiskatrin! - fixed schedule handling and added tests for this bug
-
#689
64a6ac3Thanks @mattzcarey! - add patch to fix mcp sdk oauth discovery fallback to root domain for some servers (better-auth powered) -
#681
0035951Thanks @threepointone! - update dependencies -
#684
5e80ca6Thanks @threepointone! - fix: make agents cli actually run
- #679
e173b41Thanks @whoiskatrin! - enhance request ID tracking and stream handling in useAgentChat
-
#673
603b825Thanks @whoiskatrin! - added resumable streaming with minimal setup -
#665
4c0838aThanks @threepointone! - Add default JSON schema validator to MCP client -
#664
36d03e6Thanks @threepointone! - Refactor MCP server table management in Agent classMoved creation and deletion of the cf_agents_mcp_servers table from AgentMCPClientStorage to the Agent class. Removed redundant create and destroy methods from AgentMCPClientStorage and updated MCPClientManager to reflect these changes. Added comments to clarify usage in demo and test code.
-
#653
412321bThanks @deathbyknowledge! - Allowthis.destroyinside a schedule by including adestroyedflag and yieldingctx.abortinstead of calling it directly Fix issue where schedules would not be able to run for more 30 seconds due toblockConccurencyWhile.alarm()isn't manually called anymore, getting rid of the bCW. Fix an issue where immediate schedules (e.g.this.schedule(0, "foo"))) would not get immediately scheduled. -
#652
c07b2c0Thanks @mattzcarey! - ### New FeaturesMCPClientManagerAPI changes:- New
registerServer()method to register servers (replaces part ofconnect()) - New
connectToServer()method to establish connection (replaces part ofconnect()) connect()method deprecated (still works for backward compatibility)
- New
- Connection state observability: New
onServerStateChanged()event for tracking all server state changes - Improved reconnect logic:
restoreConnectionsFromStorage()handles failed connections
- Fixed failed connections not being recreated on restore
- Fixed redundant storage operations during connection restoration
- Fixed potential OAuth storage initialization issue by excluding non-serializable authProvider from stored server options
- Added defensive checks for storage initialization in MCPClientManager and DurableObjectOAuthClientProvider
- Fixed initialization order: MCPClientManager is now created AFTER database tables are created to prevent possible table-not-found errors during DO restart
-
#678
cccbd0fThanks @whoiskatrin! - convert internal AI SDK stream events to UIMessageStreamPart format -
#672
7c9f8b0Thanks @mattzcarey! - -MCPClientConnection.init()no longer triggers discovery automatically. Discovery should be done viadiscover()or throughMCPClientManager.discoverIfConnected()- New
discover()method onMCPClientConnectionwith full lifecycle management:- Handles state transitions (CONNECTED → DISCOVERING → READY on success, CONNECTED on failure)
- Supports cancellation via AbortController (cancels previous in-flight discovery)
- Configurable timeout (default 15s)
- New
cancelDiscovery()method to abort in-flight discoveries - New
discoverIfConnected()onMCPClientManagerfor simpler capability discovery per server createConnection()now returns the connection object for immediate use- Created
MCPConnectionStateenum to formalize possible states:idle,connecting,authenticating,connected,discovering,ready,failed
- Fixed discovery hanging on repeated requests - New discoveries now cancel previous in-flight ones via AbortController
- Fixed Durable Object crash-looping -
restoreConnectionsFromStorage()now starts connections in background (fire-and-forget) to avoid blockingonStartand causingblockConcurrencyWhiletimeouts - Fixed OAuth callback race condition - When
auth_urlexists in storage during restoration, state is set to AUTHENTICATING directly instead of callingconnectToServer()which was overwriting the state - Set discovery timeout to 15s
- MCP Client Discovery failures now throw errors immediately instead of continuing with empty arrays
- Added "connected" state to represent a connected server with no tools loaded yet
- New
-
#654
a315e86Thanks @mattzcarey! - When handling MCP server requests use relatedRequestId in TransportOptions to send the response down a POST stream if supported (streamable-http) -
#661
93589e5Thanks @naji247! - fix: add session ID and header support to SSE transportThe SSE transport now properly forwards session IDs and request headers to MCP message handlers, achieving closer header parity with StreamableHTTP transport. This allows MCP servers using SSE to access request headers for session management.
-
#659
48849beThanks @threepointone! - update dependencies
- #649
e135cf5Thanks @mattzcarey! - fix auth url not being cleared on a successful oauth callback causing endless reconnection
-
#637
1e3b8c9Thanks @mattzcarey! - Removed client edge transports and added deprecation warnings to update imports to the mcp typescript sdk -
#641
b2187b4Thanks @threepointone! - update dependencies
-
#631
6ddabb7Thanks @ghostwriternr! - Handle OAuth errors and validate redirect URLs -
#626
cec3ccaThanks @mattzcarey! - Remove url field from RequestExtra in WorkerTransport. It is non standard and goes against the MCP spec types. -
#630
636aaf9Thanks @ghostwriternr! - Fix OAuth redirect handling in MCP clients -
#624
3bb54bfThanks @threepointone! - Add CLI entry point and tests for agents packageIntroduces a new CLI for the agents package using yargs with the following commands (currently stubs, not yet implemented):
init/create- Initialize an agents projectdev- Start development serverdeploy- Deploy agents to Cloudflaremcp- The agents mcp server
Adds CLI test suite with comprehensive coverage for all commands and configurations. Updates package.json to register the CLI binary, adds test scripts for CLI testing, and includes yargs dependencies.
- #619
e7d0d4dThanks @mattzcarey! - Adds request info to the extra argument in onmessage. Adds a url parm which we will try push upstream to the MCP SDK as it is useful with OpenAI Apps SDK
-
#607
c9b76cdThanks @threepointone! - Add jurisdiction support to MCP agent and handlersIntroduces a
jurisdictionoption to MCP agent server and streaming/SSE handlers, allowing Durable Object instances to be created in specific geographic regions for compliance (e.g., GDPR). Documentation updated to explain usage and available jurisdictions.
-
#602
aed8e18Thanks @threepointone! - Add CORS support to MCP handler and testsIntroduces CORS configuration to experimental_createMcpHandler, including handling OPTIONS preflight requests and adding CORS headers to responses and errors. Exports corsHeaders from utils. Adds comprehensive tests for CORS behavior in handler.test.ts.
-
#603
4da191cThanks @mattzcarey! - Drop the experimental_ prefix on createMcpHandler
- #592
8e9d714Thanks @mattzcarey! - Fix oauth2 client again
- #578
829866cThanks @threepointone! - udpate dependencies
-
#582
a215bb2Thanks @mattzcarey! - chore: remove main field from agents package.json -
#576
026696fThanks @mattzcarey! - createMcpHandler for stateless MCP Worker
- #566
7f4616cThanks @mattzcarey! - fix: Oauth2 client flow
- #531
cdfc590Thanks @whoiskatrin! - update our cache key in useAgentChat to include agent name (fix for #420)
- #559
3667584Thanks @threepointone! - use lazy imports for ai sdk
-
#554
2cc0f02Thanks @threepointone! - update dependencies -
#554
2cc0f02Thanks @threepointone! - move to tsdown, slim down generated bundles
-
#550
336602fThanks @ainergiz! - encode MCP message headers with Base64 -
#544
afd9efdThanks @whoiskatrin! - Startup time optimisations
- #545
70499f1Thanks @deathbyknowledge! - Update mcp sdk
-
#527
b060233Thanks @deathbyknowledge! - remove isToolCallInProgress -
#535
75865ebThanks @threepointone! - move x402 to peerDependencies -
#525
789141eThanks @whoiskatrin! - use INSERT OR REPLACE for message persistence to allow tool call updates -
#529
c41ebbcThanks @deathbyknowledge! - persist and stream reply in saveMessages
-
#521
1bd0c75Thanks @ghostwriternr! - Fix OAuth state parameter security vulnerability by replacing client_id with secure random tokens -
#524
06b2ab0Thanks @threepointone! - update dependencies
b388447Thanks @threepointone! - fix: getAITools shouldn't include hyphens in tool names
a90de5dThanks @threepointone! - codemode: remove stray logs, fix demo
9a8fed7Thanks @threepointone! - update deps
-
#458
d3e7a68Thanks @whoiskatrin! - Add unified async authentication support to useAgent hook The useAgent hook now automatically detects and handles both sync and async query patterns -
#512
f9f03b4Thanks @threepointone! - codemode: a tool that generates code to run your tools -
#499
fb62d22Thanks @deathbyknowledge! - handle all message types in the reply streaming handler -
#509
71def6bThanks @ghostwriternr! - Fix OAuth authentication for MCP servers and add transport configuration- Fix authorization codes being consumed during transport fallback
- Add transport type option to addMcpServer() for explicit control
- Add configurable OAuth callback handling (redirects, custom responses)
- Fix callback URL persistence across Durable Object hibernation
- #504
da56baaThanks @threepointone! - fix attribution
5969a16Thanks @threepointone! - trigger a release
- #495
ff9329fThanks @ghostwriternr! - Fix OAuth callback handling and add HOST auto-detection- Fix OAuth callback "Not found" errors by removing MCPClientManager override
- Add OAuth callback URL persistence across Durable Object hibernation
- Fix OAuth connection reuse during reconnect to prevent state loss
- Add OAuth transport tracking to prevent authorization code consumption during auto-fallback
- Preserve PKCE verifier across transport attempts
- Make callbackHost parameter optional with automatic request-based detection
- Add URL normalization for consistent transport endpoint handling
-
#465
6db2cd6Thanks @BeiXiao! - fix(ai-react): prevent stale agent capture in aiFetch; ensure active connection is used -
#440
9ef35e2Thanks @axuj! - fix: pass agent._pk as id to useChat to prevent stale WebSocket instances
-
#492
00ba881Thanks @threepointone! - fix: this.mcp.getAITools now includes outputSchema -
#494
ecbd795Thanks @threepointone! - update deps
-
#478
8234d41Thanks @deathbyknowledge! - Refactor streamable HTTP transport -
#486
4abd78aThanks @threepointone! - fix: don't context wrap methods on Agents that have already been wrapped -
#480
23db655Thanks @deathbyknowledge! - Update mcp tools and client for x402 support
-
#470
28013baThanks @deathbyknowledge! - Store initialize requests and set them in onStart -
#467
b8eba58Thanks @deathbyknowledge! - Silently handle writer close errors -
bfc9c75Thanks @whoiskatrin! - add response metadata -
#469
fac1fe8Thanks @umgefahren! - Include reasoning parts in finalized and persistet message. -
#472
2d0d2e1Thanks @deathbyknowledge! - use header for session ids in streamable http GET streams -
7d9b939Thanks @threepointone! - update dependencies
- #459
0ffa9ebThanks @whoiskatrin! - update mcp sdk
- #415
f7bd395Thanks @deathbyknowledge! - Make McpAgent extend Agent + Streaming HTTP protocol features
-
#451
9beccddThanks @threepointone! - udpate dependencies -
#447
3e523eaThanks @whoiskatrin! - add support for plain text responses alongside SSE streaming
- #391
ecf8926Thanks @whoiskatrin! - update to ai sdk v5
-
#445
14616d3Thanks @deathbyknowledge! - Fix MCP client to treatclient_urias a valid URL -
#410
25b261eThanks @amorriscode! - docs: minor fixes -
2684adeThanks @threepointone! - update deps -
01b919dThanks @threepointone! - remove unstable_ prefixes with deprecation warningsThis deprecates all unstable_ prefixes with deprecation warnings. Specifically:
- unstable_callable -> callable
- unstable_getAITools -> getAITools
- unstable_getSchedulePrompt -> getSchedulePrompt
- unstable_scheduleSchema -> scheduleSchema
Using the unstable_ prefixed versions will now emit a deprecation warning. In the next major version, the unstable_ prefixed versions will be removed.
-
#434
f0c6dceThanks @threepointone! - don't autowrap getters on an agent -
#446
696d33eThanks @Flouse! - fix: use Object.getOwnPropertyDescriptor for property check -
1e4188cThanks @threepointone! - update workers-ai-provider -
#436
8dac62cThanks @deathbyknowledge! - Fix onConnect race condition -
#409
352d62cThanks @MrgSub! - Refactor message types to use enum in AIChatAgent and related files -
#442
0dace6eThanks @threepointone! - fix: don't wrap a method with an agent context if it's already wrapped
fd59ae2Thanks @threepointone! - fix: prefix mcp tool names with tool_
-
#404
2a6e66eThanks @threepointone! - udpate dependencies -
#404
2a6e66eThanks @threepointone! - log less dataas part of our observability impl, we were logging way too much data, making it a probable data leak, but also blowing past the max size limit on o11y messages. This reduces the amount of data logged.
0cf8e80Thanks @threepointone! - trigegr a release
-
#392
669a2b0Thanks @Maximo-Guk! - fix: Ensure McpAgent props stay current -
#394
e4a2352Thanks @threepointone! - update state incrementally as mcp servers connect -
#390
b123357Thanks @threepointone! - update (most) dependencies -
#376
1eac06eThanks @whoiskatrin! - add elicitation support and examples -
3bcb134Thanks @threepointone! - update partysocket -
#374
b63b4a6Thanks @laulauland! - Improve MCP client connection resilience with Promise.allSettled -
#378
c69f616Thanks @amorriscode! - add auto transport option -
#387
8c2713fThanks @whoiskatrin! - Fix/mcp agent error handling
- #372
a45f8f3Thanks @threepointone! - default Agent's Env to cloudflare's Env
- #357
40bd73cThanks @davemurphysf! - Pass incoming headers to the DO fetch method
- #364
885b3dbThanks @whoiskatrin! - add HTTP Streamable support
- #359
14bb798Thanks @ghostwriternr! - Fix email routing to be case-insensitive for agent names
-
#354
f31397cThanks @jahands! - fix: dequeue items in DB after each task is completePrevents a single failure from causing all items in the queue from being retried (including previously processed items that were successful).
-
#319
e48e5f9Thanks @threepointone! - add lightweight .queue -
#352
0bb74b8Thanks @threepointone! - email adaptor -
#345
c5e3a32Thanks @whoiskatrin! - Add automatic context wrapping for custom Agent methods
- #350
70ed631Thanks @ghostwriternr! - Fix TypeScript types resolution by reordering export conditions
- #339
22d140bThanks @threepointone! - udpate dependencies
- #331
7acfd65Thanks @geelen! - Adding a new MCP header to the CORS allowlist to follow the updated spec
b4ebb44Thanks @threepointone! - update dependencies
efffe3eThanks @threepointone! - trigger release
- #325
7e0777bThanks @threepointone! - update deps
- #316
7856b4dThanks @whoiskatrin! - Add fallback message when agent returns no response
-
9c6b2d7Thanks @threepointone! - update deps -
#311
8a4558cThanks @threepointone! - Added a call tothis.ctx.abort('destroyed')in thedestroymethod to ensure the agent is properly evicted during cleanup.
- #302
b57e1d9Thanks @cmsparks! - Fix an error where MCP servers pending connection would trigger an error
- #299
eeb70e2Thanks @courtney-sims! - Prevent auth url from being regenerated during oauth flow
7972da4Thanks @threepointone! - update deps
- #295
cac66b8Thanks @threepointone! - duck typing DurableObjectNamespace type
-
87b44abThanks @threepointone! - update deps -
#292
aacf837Thanks @cmsparks! - Fix issue where stray MCP connection state is left after closing connection
-
#289
86cae6fThanks @ruifigueira! - Type-safe serializable RPC methods -
#287
94d9a2eThanks @ruifigueira! - Improve agent types
- #283
041b40fThanks @ruifigueira! - Improve Agent stub
- #274
93ccdbdThanks @ruifigueira! - Stub for Agent RPC
-
#273
d1f6c02Thanks @cmsparks! - Expose getMcpServerState internally in agent -
#276
b275deaThanks @ruifigueira! - Fix non-optional parameters after undefined ones -
#279
2801d35Thanks @threepointone! - rename getMcpServerState/getMcpServers
- #269
0ac89c6Thanks @ruifigueira! - Add type support to react useAgent().call
- #270
d6a4edaThanks @threepointone! - update deps
04d925eThanks @threepointone! - convert two missed #methods to a private _methods
-
#265
ac0e999Thanks @threepointone! - refactor #method/#property to private method/private property -
#267
385f0b2Thanks @threepointone! - prefix private methods/properties with _
-
#253
8ebc079Thanks @adesege! - fix: allow overriding fetch and request headers in SSEEdgeClientTransport -
#260
ca44ae8Thanks @nickfujita! - Update Agent.alarm to readonly, linking to schedule-task docs -
#261
881f11eThanks @geelen! - Addingmcp-session-idto McpAgents' CORS headers to permit web-based MCP clients
-
#258
eede2bdThanks @threepointone! - wrap onRequest so getCurrentAgent worksFixes #256
-
#249
c18c28aThanks @dexxiez! - chore: add top level default types to package.json -
#246
c4d53d7Thanks @jmorrell-cloudflare! - Ensure we are passing ctx.props to McpAgent for the Streamable transport -
#251
96a8138Thanks @brettimus! - Ensure isLoading is false after youstopan ongoing chat agent request
-
#242
c8f53b8Thanks @threepointone! - update deps -
#240
9ff62edThanks @threepointone! - mcp: Log when an error is caught inside onSSEMcpMessage -
#239
7bd597aThanks @sushichan044! - fix(types): explicitly annotate this with void to avoid unbound method warning
6c24007Thanks @threepointone! - Revert "fool typescript into thinking agent will always be defined in ge…
ad0054bThanks @threepointone! - fool typescript into thinking agent will always be defined in getCurrentAgent()
- #231
ba99b7cThanks @threepointone! - update deps to pick up a potential fix for onStart not firing
a25eb55Thanks @threepointone! - don't throw if no current agent
- #228
f973b54Thanks @threepointone! - mcp client: fix tool name generation
- #226
5b7f03eThanks @threepointone! - mcp client: closeConnection(id) and closeAllConnections()
- #224
b342dcfThanks @threepointone! - getCurrentAgent()
-
#222
44dc3a4Thanks @threepointone! - prepend mcp tool names with server id, use nanoid everywhere -
#221
f59e6a2Thanks @ruifigueira! - Support server as promises in McpAgent
- #219
aa5f972Thanks @jmorrell-cloudflare! - Fix type error for McpAgent.serve and McpAgent.serveSSE
-
#215
be4b7a3Thanks @threepointone! - update deps -
843745dThanks @threepointone! - Thanks @brettimus for #105: Propagate cancellation signals from useAgentChat to ChatAgent -
#217
8d8216cThanks @threepointone! - Add .mcp to the Agent class, and add a helper to McpClientManager to convert tools to work with AI SDK -
#212
5342ce4Thanks @pbteja1998! - do not remove search params and hash from mcp endpoint message
-
#205
3f532baThanks @threepointone! - Let .server on McpAgent be a Server or McpServer -
#208
85d8eddThanks @a-type! - Fix: resolved a problem in useAgentChat where initial messages would be refetched on re-render when using React StrictMode
- #206
0c4b61cThanks @threepointone! - mcp client: result schema and options are optional
-
#202
1e060d3Thanks @jmorrell-cloudflare! - await stream writer calls in websocket handlers -
#199
717b21fThanks @pauldraper! - Add missing dependencies to agents -
#203
f5b5854Thanks @jmorrell-cloudflare! - Jmorrell/fix streamable hibernation issue -
#186
90db5baThanks @jmorrell-cloudflare! - Rename McpAgent.mount to McpAgent.serveSSE with McpAgent.mount serving as an alias for backward compatibility -
#186
90db5baThanks @jmorrell-cloudflare! - Update dependencies
- #197
b30ffdaThanks @threepointone! - fix websocket missing message trigger
-
#196
ba5a5feThanks @threepointone! - expose persistMessages on AIChatAgent -
#126
1bfd6a7Thanks @nickfujita! - Add ai-types to esm exports
- #173
49fb428Thanks @cmsparks! - fix: require authProvider on client connect and handle client "Method not found" initialization errors
- #168
2781f7dThanks @threepointone! - update deps
-
33b22feThanks @threepointone! - don't import WorkflowEntrypointfixes #166
-
#163
956c772Thanks @brishin! - Fix: Missing agent dep in useCallback -
#164
3824fd4Thanks @threepointone! - revert #161
- #161
1f6598eThanks @threepointone! - mcp: remove duplicate agent init, await root .init()
- #159
b8377c1Thanks @jmorrell-cloudflare! - Fix issues with McpAgent and setState introduced by hibernation changes
-
#140
2f5cb3aThanks @cmsparks! - Remote MCP Client with auth supportThis PR adds:
- Support for authentication for MCP Clients (Via a DO based auth provider)
- Some improvements to the client API per #135
- A more in depth example of MCP Client, which allows you to add any number of remote MCP servers with or without auth
- #149
49e8b36Thanks @irvinebroque! - Automatically change "/" path to "/*" in MCP server mount() method
- #151
e376805Thanks @threepointone! - useAgent: don't throw whenqueryis an async url provider
- #146
316f98cThanks @threepointone! - remove lowercase warning for agent names
- #142
1461795Thanks @threepointone! - ai-chat-agent: pass query params correctly in /get-messages
- #138
3bbbf81Thanks @geelen! - Fixed internal build issue that caused incomplete package to be published
-
#125
62d4e85Thanks @cmsparks! - MCP Client x Agents Implementation -
#128
df716f2Thanks @jmorrell-cloudflare! - MCP: Hibernate-able transport -
#137
c3e8618Thanks @threepointone! - convert inputagentin clients to kebab-case as expected by the server
-
#133
6dc3b6aThanks @threepointone! - remove description as an arg from getSchedules -
#130
7ff0509Thanks @threepointone! - update deps
7c40201Thanks @threepointone! - mark context as unstable_
-
#122
d045755Thanks @threepointone! -import {context} from 'agents';Export the current agent, request, and connection from a shared context. Particularly useful for tool calls that might not have access to the current agent in their module scope.
-
#118
6e66bd4Thanks @max-stytch! - fix: Pass Env param thru to DurableObject definition -
#121
82d5412Thanks @threepointone! - update deps
-
#111
eb6827aThanks @threepointone! - update depsreplace the beta release of partysocket with a real one
-
#107
4f3dfc7Thanks @threepointone! - update deps, allow sub/path/prefix, AND_BINDINGS_LIKE_THISof note,
- the partyserver update now allows for prefixes that/have/sub/paths
- bindings THAT_LOOK_LIKE_THIS are correctly converted to kebabcase now
-
#106
1d1b74cThanks @geelen! - Adding the first iteration of McpAgent -
#103
9be8008Thanks @threepointone! - update deps
- #100
ee727caThanks @danieljvdm! - Pass state generic throughuseAgentChat
- #96
d7d2876Thanks @threepointone! - update deps
- #94
fb4d0a6Thanks @threepointone! - better error handling (based on #65 by @elithrar)- implement
this.onErrorfor custom error handling - log errors from more places
- catch some missed async errors and log them
- mark some methods as actually private
- implement
- #92
fbaa8f7Thanks @threepointone! - Renamed agents-sdk -> agents
- #74
7bcdd83Thanks @gingerhendrix! - Replace discriminatedUnion with simple object for Gemini models
- #88
7532166Thanks @threepointone! - passcors:truetorouteAgentRequestto automatically use across domains
39197abThanks @threepointone! - removecf_agent_chat_initmessage
-
#85
acbc34eThanks @threepointone! - Add RPC support withunstable_callabledecorator for method exposure. This feature enables:- Remote procedure calls from clients to agents
- Method decoration with
@unstable_callableto expose agent methods - Support for both regular and streaming RPC calls
- Type-safe RPC calls with automatic response handling
- Real-time streaming responses for long-running operations
Note: The
callabledecorator has been renamed tounstable_callableto indicate its experimental status.
-
#83
a9248c7Thanks @threepointone! - add state sync to the regular agent clientfixes #9
2c077c7Thanks @threepointone! - warn if agent/name passed to client isn't in lowercase
db70cebThanks @threepointone! - fix async/await error for useAgentChat
-
#79
1dad549Thanks @threepointone! - clear initial message cache on unmount, add getInitialMessagesThis clears the initial messages cache whenever useAgentChat is unmounted. Additionally, it adds a getInitialMessages option to pass your own custom method for setting initial messages. Setting getInitialMessages:null disables any fetch for initial messages, so that the user can populate initialMessages by themselves if they'd like.
I also added a chat example to the playground.
-
8ade3afThanks @threepointone! - export Schedule type -
#77
82f277dThanks @threepointone! - pass credentials to get-messages call
5b96c8aThanks @threepointone! - unstable_ scheduling prompt helper shouldn't take input text
-
06c4386Thanks @threepointone! - update deps -
#62
2d680f3Thanks @threepointone! - unstable_ scheduling helpers -
48ff237Thanks @threepointone! - (for @sam-goodwin, #58) fix: pass headers to /get-messages
- #53
877d551Thanks @deathbyknowledge! - fix onMessage not getting called
-
#46
6efb950Thanks @threepointone! - update deps -
#49
653ebadThanks @threepointone! - add linting, fix a bunch of bugs.
-
#37
ff0679fThanks @threepointone! -Agent::initialStateYou can now set an initial state for an agent
type State = { counter: number; text: string; color: string; }; class MyAgent extends Agent<Env, State> { initialState = { counter: 0, text: "", color: "#3B82F6", }; doSomething() { console.log(this.state); // {counter: 0, text: "", color: "#3B82F6"}, if you haven't set the state yet } }
As before, this gets synced to useAgent, so you can do:
const [state, setState] = useState<State>(); const agent = useAgent<State>({ agent: "my-agent", onStateUpdate: (state) => { setState(state); }, });
9938444Thanks @threepointone! - scheduling: do a typecheck/throw error if not a valid method on this
7149fd2Thanks @threepointone! - don't log when state updates on the server
54962feThanks @threepointone! - trigger a release
-
d798d99Thanks @threepointone! - don't bork if connection disconnects -
fd17e02Thanks @threepointone! - respond to server saved messages -
90fe787Thanks @threepointone! - fix scheduler implementation/types
9075920Thanks @threepointone! - change onChatMessage signature
-
2610509Thanks @threepointone! - Hono Agents -
7a3a1a0Thanks @threepointone! - AgentContext
066c378Thanks @threepointone! - setState() doesn't take source anymore
2864acfThanks @threepointone! - chat agent can now saveMessages explicitly
7035ef5Thanks @threepointone! - trigger a release
8335b4bThanks @threepointone! - fix some types
619dac5Thanks @threepointone! - new useChat, with multiplayer, syncing, persistence; updated HITL guide with useChat
0680a02Thanks @threepointone! - remove email mentions from readme
acbd0f6Thanks @threepointone! - .state/.setState/.onStateUpdate
7dab6bcThanks @threepointone! - more on agentFetch
411c149Thanks @threepointone! - actually fix client fetch
40bfbefThanks @threepointone! - fix client.fetch
3f1ad74Thanks @threepointone! - export some types, use a default agent name
eaba262Thanks @threepointone! - do a release