Cloudflare's Agents SDK lets MCP servers ask users questions mid-task!
Hey everyone, it's me, Shii-chan! Today I found a jam-packed update to Cloudflare's Agents SDK, so let me walk you through it!
Cloudflare ChangelogWhat was announced?
On the Cloudflare Changelog, the latest releases of @cloudflare/agents were announced together. They bring big improvements to MCP transport protocol support and agent connectivity. Here are the highlights.
- MCP elicitation support (servers can ask users for confirmation mid-tool-execution)
- HTTP streamable transport support for MCP
- Enhanced MCP connectivity (auto transport selection, better error handling)
- A lightweight
.queue()for background task deferral - A new email adapter for receiving and auto-replying to emails
- Automatic context wrapping for custom methods
The story so far
Until now, MCP servers mostly just returned a result once a tool was called, with no easy way to pause mid-execution and ask the user something like "are you sure?". Building confirmations, forms, or multi-step interactions required extra workarounds.
Transport was centered on SSE (Server-Sent Events), which left room for improvement in streaming efficiency and connection stability. On top of that, there wasn't a built-in way to defer expensive work to run in the background inside an agent, or to receive and automatically respond to email.
What changes
Developers building MCP servers can now weave confirmation dialogs and form-like, interactive workflows naturally into the MCP flow. Even if the agent hibernates while waiting on user input, the elicitation state is preserved, so things pick back up smoothly.
On the transport side, the SDK now automatically picks the best available method, so developers get more efficient and stable communication without having to micromanage it. Agents can also offload expensive work to a background queue, and now receive email and reply automatically — meaningfully expanding what an agent can do.
Dive Deep
MCP Elicitation
To ask the user for confirmation during tool execution, you call this.elicitInput(). You describe the shape of the requested input with a JSON Schema, like this:
// Request user confirmation via elicitation
const confirmation = await this.elicitInput({
message: `Are you sure you want to increment the counter by ${amount}?`,
requestedSchema: {
type: "object",
properties: {
confirmed: {
type: "boolean",
title: "Confirm increment",
description: "Check to confirm the increment",
},
},
required: ["confirmed"],
},
});
The elicitation state is saved to durable storage, so even if the agent hibernates while waiting for a response, the interaction can continue properly after it wakes up.
HTTP streamable transport
MCP now supports HTTP streamable transport, recommended over SSE. Per the source, the benefits are:
- More efficient data streaming with reduced overhead
- Improved connection stability and error recovery
- Automatic fallback to SSE when streamable transport isn't available
On the server side, it looks like this:
export default MyMCP.serve("/mcp", {
binding: "MyMCP",
});
The SDK automatically picks the best transport method available, gracefully falling back from streamable-http to SSE when needed.
Enhanced MCP connectivity
Along with automatic transport selection, connection state management and error reporting have been improved. Agent property updates are also now centralized, keeping things consistent across different contexts.
Lightweight .queue() for task deferral
A new .queue() method lets you push expensive work into the background. Calling it doesn't block execution, and queued tasks run one after another:
class MyAgent extends Agent {
doSomethingExpensive(payload) {
// a long running process that you want to run in the background
}
queueSomething() {
await this.queue("doSomethingExpensive", somePayload); // this will NOT block further execution, and runs in the background
await this.queue("doSomethingExpensive", someOtherPayload); // the callback will NOT run until the previous callback is complete
// ... call as many times as you want
}
}
It's a good fit for things like processing user messages or sending notifications. The source notes that all you need to do is define a method like processMessage on your agent to start using it.
New email adapter
You can now build an agent that receives and automatically replies to email, just by implementing the onEmail lifecycle method.
export class EmailAgent extends Agent {
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
// create a response based on the email contents
// and then send a reply
await this.replyToEmail(email, {
fromName: "Email Agent",
body: `Thanks for your email! You've sent us "${parsed.subject}". We'll process it shortly.`,
});
}
}
For routing incoming mail, you use routeAgentEmail together with a resolver that decides which agent handles which address.
export default {
async email(email, env) {
await routeAgentEmail(email, env, {
resolver: createAddressBasedEmailResolver("EmailAgent"),
});
},
};
Automatic context wrapping for custom methods
Custom methods are now automatically wrapped with the agent's context. Previously, getCurrentAgent() didn't reliably work inside RPC calls, but now it works correctly no matter where it's called from.
export class MyAgent extends Agent {
async suggestReply(message) {
// getCurrentAgent() now correctly works, even when called inside an RPC method
const { agent } = getCurrentAgent()!;
return generateText({
prompt: `Suggest a reply to: "${message}" from "${agent.name}"`,
tools: [replyWithEmoji],
});
}
}
Wrap-up
- Elicitation lets MCP servers request user confirmation mid-tool-execution, with state preserved through hibernation
- HTTP streamable transport, recommended over SSE, is now supported, with automatic transport selection and fallback
- MCP connection error handling and property-update consistency have both improved
- A lightweight
.queue()method lets you defer expensive work to the background onEmailplusrouteAgentEmaillet you build agents that receive and auto-reply to email- Automatic context wrapping makes
getCurrentAgent()reliable even inside custom RPC methods
If you're building MCP servers or agents on Cloudflare Workers, this is an update you could realistically try out next week!