|
| 1 | +--- |
| 2 | +title: Agents SDK v0.2.22 - MCP client transport deprecation and stateful MCP handler support |
| 3 | +description: This release deprecates client edge transports in favor of the official MCP TypeScript SDK transports, adds support for stateful createMcpHandler usage, and includes a new elicitation example demonstrating interactive input collection in MCP servers. |
| 4 | +products: |
| 5 | + - agents |
| 6 | + - workers |
| 7 | +date: 2025-11-13 |
| 8 | +--- |
| 9 | + |
| 10 | +We've shipped a new release for the [Agents SDK](https://github.com/cloudflare/agents) that streamlines MCP client transport usage, enhances MCP server capabilities with stateful handlers, and demonstrates best practices for interactive user input. |
| 11 | + |
| 12 | +## MCP Client Transport Deprecation |
| 13 | + |
| 14 | +The custom edge transport implementations (`SSEEdgeClientTransport` and `StreamableHTTPEdgeClientTransport`) have been deprecated in favor of the official transports from the MCP TypeScript SDK. |
| 15 | + |
| 16 | +### Migration Guide |
| 17 | + |
| 18 | +If you're using the deprecated transports, update your imports: |
| 19 | + |
| 20 | +**Before:** |
| 21 | +```ts |
| 22 | +import { SSEEdgeClientTransport } from "agents/mcp"; |
| 23 | +import { StreamableHTTPEdgeClientTransport } from "agents/mcp"; |
| 24 | +``` |
| 25 | + |
| 26 | +**After:** |
| 27 | +```ts |
| 28 | +import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; |
| 29 | +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; |
| 30 | +``` |
| 31 | + |
| 32 | +The deprecated classes will continue to work with deprecation warnings until the next major version, giving you time to migrate. The official MCP SDK transports provide the same functionality with better compatibility and ongoing support from the MCP community. |
| 33 | + |
| 34 | +## Stateful MCP Handler Support |
| 35 | + |
| 36 | +The `createMcpHandler` function now supports stateful usage patterns, allowing you to build MCP servers that persist state across requests using Durable Objects storage. |
| 37 | + |
| 38 | +This is particularly useful when: |
| 39 | +- Building MCP servers with the raw `@modelcontextprotocol/sdk` instead of the `McpAgent` class |
| 40 | +- Implementing custom session management |
| 41 | +- Supporting MCP features that require persistent state, such as elicitation |
| 42 | + |
| 43 | +### Example: Stateful MCP Server |
| 44 | + |
| 45 | +```ts |
| 46 | +import { createMcpHandler, WorkerTransport, type TransportState } from "agents/mcp"; |
| 47 | +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; |
| 48 | +import { Agent } from "agents"; |
| 49 | + |
| 50 | +const STATE_KEY = "mcp_transport_state"; |
| 51 | + |
| 52 | +export class MyAgent extends Agent<Env, State> { |
| 53 | + server = new McpServer({ |
| 54 | + name: "stateful-server", |
| 55 | + version: "1.0.0" |
| 56 | + }); |
| 57 | + |
| 58 | + transport = new WorkerTransport({ |
| 59 | + sessionIdGenerator: () => this.name, |
| 60 | + storage: { |
| 61 | + get: () => { |
| 62 | + return this.ctx.storage.kv.get<TransportState>(STATE_KEY); |
| 63 | + }, |
| 64 | + set: (state: TransportState) => { |
| 65 | + this.ctx.storage.kv.put<TransportState>(STATE_KEY, state); |
| 66 | + } |
| 67 | + } |
| 68 | + }); |
| 69 | + |
| 70 | + async onMcpRequest(request: Request) { |
| 71 | + return createMcpHandler(this.server, { |
| 72 | + transport: this.transport |
| 73 | + })(request, this.env, {} as ExecutionContext); |
| 74 | + } |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +The `WorkerTransport` integrates with Durable Objects storage to maintain session state, enabling features like elicitation that require bidirectional communication. |
| 79 | + |
| 80 | +## MCP Elicitation Example |
| 81 | + |
| 82 | +This release includes a new [elicitation example](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) demonstrating how to build interactive MCP tools that collect additional input from users during tool execution. |
| 83 | + |
| 84 | +Elicitation is useful for: |
| 85 | +- Collecting user confirmation before performing actions |
| 86 | +- Gathering additional parameters not included in the initial request |
| 87 | +- Building multi-step workflows with user interaction |
| 88 | +- Implementing "human-in-the-loop" patterns |
| 89 | + |
| 90 | +### Example: Interactive Counter Tool |
| 91 | + |
| 92 | +```ts |
| 93 | +this.server.registerTool( |
| 94 | + "increase-counter", |
| 95 | + { |
| 96 | + description: "Increase the counter", |
| 97 | + inputSchema: { |
| 98 | + confirm: z.boolean().describe("Do you want to increase the counter?") |
| 99 | + } |
| 100 | + }, |
| 101 | + async ({ confirm }) => { |
| 102 | + if (!confirm) { |
| 103 | + return { |
| 104 | + content: [{ type: "text", text: "Counter increase cancelled." }] |
| 105 | + }; |
| 106 | + } |
| 107 | + |
| 108 | + // Use elicitation to ask for the amount |
| 109 | + const basicInfo = await this.server.server.elicitInput({ |
| 110 | + message: "By how much do you want to increase the counter?", |
| 111 | + requestedSchema: { |
| 112 | + type: "object", |
| 113 | + properties: { |
| 114 | + amount: { |
| 115 | + type: "number", |
| 116 | + title: "Amount", |
| 117 | + description: "The amount to increase the counter by", |
| 118 | + minLength: 1 |
| 119 | + } |
| 120 | + }, |
| 121 | + required: ["amount"] |
| 122 | + } |
| 123 | + }); |
| 124 | + |
| 125 | + if (basicInfo.action !== "accept" || !basicInfo.content) { |
| 126 | + return { |
| 127 | + content: [{ type: "text", text: "Counter increase cancelled." }] |
| 128 | + }; |
| 129 | + } |
| 130 | + |
| 131 | + // Update state with the elicited amount |
| 132 | + this.setState({ |
| 133 | + ...this.state, |
| 134 | + counter: this.state.counter + Number(basicInfo.content.amount) |
| 135 | + }); |
| 136 | + |
| 137 | + return { |
| 138 | + content: [{ |
| 139 | + type: "text", |
| 140 | + text: `Counter increased by ${basicInfo.content.amount}, current value is ${this.state.counter}` |
| 141 | + }] |
| 142 | + }; |
| 143 | + } |
| 144 | +); |
| 145 | +``` |
| 146 | + |
| 147 | +The elicitation example demonstrates how to combine stateful storage with interactive input collection to build sophisticated MCP tools. |
| 148 | + |
| 149 | +## Additional Updates |
| 150 | + |
| 151 | +- Updated dependencies to their latest versions |
| 152 | +- Improved compatibility with the MCP TypeScript SDK ecosystem |
| 153 | + |
| 154 | +For more details on these changes, see the [Agents SDK changelog](https://github.com/cloudflare/agents/blob/main/packages/agents/CHANGELOG.md#0222). |
0 commit comments