Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: "Agents SDK v0.18.0: Stateless MCP SDK v2 support"
description: "Agents SDK v0.18.0 adds Stateless MCP SDK v2 clients and servers while retaining explicit Legacy support."
products:
- agents
- workers
date: 2026-07-20
---

import { PackageManagers, TypeScriptExample } from "~/components";

Agents SDK v0.18.0 adds support for the split MCP TypeScript SDK v2 packages. Agents can serve Stateless MCP, fall back to Legacy servers, and handle Stateless Elicitation.

Existing Legacy server deployments remain supported through `createLegacyMcpHandler`, `WorkerTransport`, and `McpAgent`.

## Stateless servers

`createMcpHandler` now accepts a factory that returns a server from `@modelcontextprotocol/server`. The factory creates an isolated server for each request.

<TypeScriptExample>

```ts
import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";

function createServer() {
return new McpServer({ name: "example", version: "1.0.0" });
}

export default createMcpHandler(createServer);
```

</TypeScriptExample>

The isolated `agents/mcp/server` entry keeps `McpAgent`, `WorkerTransport`, MCP client transports, and SDK v1 modules out of Stateless server bundles. Its Legacy compatibility lane supports ordinary tools, resources, and prompts. Session streams, replay, deletion, and pushed server-to-client requests still require a Legacy sessionful server.

The Workers wrapper validates present browser Origins, supports explicit delegation to trusted Origin middleware, and exposes the upstream handler's `close`, `notify`, and `bus` controls.

## Explicit Legacy support

Existing SDK v1 servers can rename `createMcpHandler` to `createLegacyMcpHandler` without changing their transport behavior:

<TypeScriptExample>

```ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { createLegacyMcpHandler } from "agents/mcp";

export default createLegacyMcpHandler(
new McpServer({ name: "legacy", version: "1.0.0" }),
);
```

</TypeScriptExample>

Passing an SDK v1 server directly to `createMcpHandler` is deprecated. `McpAgent` remains available for stateful Legacy deployments, but it is deprecated and feature-frozen.

## Client negotiation and input requests

The MCP client manager now uses `@modelcontextprotocol/client`. It probes Stateless servers with `server/discover` and falls back to the Legacy `initialize` handshake.

Stateless Elicitation uses `input_required` through multi-round-trip requests (MRTR). Legacy Elicitation uses the same form and URL handlers for pushed requests. The SDK collects input, retries the original operation, and resolves the original `callTool`, `getPrompt`, or `readResource` promise with its final result.

OAuth callbacks now validate issuer metadata through the v2 SDK. Discovery state and issuer-bound credentials persist across browser redirects and Durable Object hibernation.

### Upgrade

To update the Agents SDK:

<PackageManagers pkg="agents@latest" />

Refer to [Migrate to MCP SDK v2](/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/) for package changes, compatibility limits, and rollout steps.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ products:

import { TypeScriptExample, LinkCard } from "~/components";

When you build MCP Servers on Cloudflare, you extend the [`McpAgent` class](https://github.com/cloudflare/agents/blob/main/packages/agents/src/mcp/index.ts#L32-L620), from the Agents SDK:
`McpAgent` creates a stateful Legacy MCP server backed by a Durable Object.

:::caution[Deprecated]

`McpAgent` remains available for Legacy servers that use stateful features. It is deprecated and feature-frozen. A server without those dependencies can migrate directly to [`createMcpHandler`](/agents/model-context-protocol/apis/handler-api/).

A server that depends on MCP session state, RPC, pushed server-to-client requests, standalone streams, or replay needs a staged migration. Design Stateless equivalents, add a Stateless route, and serve both lanes until clients migrate and existing sessions drain.

Keep importing the Legacy server `McpServer` from `@modelcontextprotocol/sdk`. An SDK v2 server from `@modelcontextprotocol/server` cannot run inside `McpAgent`.

Refer to [Migrate to MCP SDK v2](/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/) for the feature mapping and dual-era rollout.

:::

<TypeScriptExample>

Expand Down Expand Up @@ -40,7 +52,7 @@ export class MyMCP extends McpAgent {

This means that each instance of your MCP server has its own durable state, backed by a [Durable Object](/durable-objects/), with its own [SQL database](/agents/runtime/lifecycle/state/).

Your MCP server doesn't necessarily have to be an Agent. You can build MCP servers that are stateless, and just add [tools](/agents/model-context-protocol/protocol/tools/) to your MCP server using the `@modelcontextprotocol/sdk` package.
A Stateless server can define [tools](/agents/model-context-protocol/protocol/tools/) with `@modelcontextprotocol/server` and serve them through `createMcpHandler`.

But if you want your MCP server to:

Expand Down Expand Up @@ -257,9 +269,9 @@ export class MyMCP extends McpAgent<Env, State, {}> {

</TypeScriptExample>

## Elicitation
## Legacy Elicitation

[MCP elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation) lets a server request user input while handling another request, such as a tool call. The current stable MCP specification defines two modes:
[MCP elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation) lets a server request user input while handling another request, such as a tool call. Legacy Elicitation defines two modes:

- **Form mode** collects structured, non-sensitive data through the client.
- **URL mode** sends the user to an out-of-band interaction, such as third-party authorization or payment.
Expand Down Expand Up @@ -371,7 +383,7 @@ switch (result.action) {
</TypeScriptExample>

:::note[MCP client support]
Not all MCP clients implement elicitation. Check the client before depending on it and provide a fallback when appropriate. Agents acting as MCP clients can handle both modes through [MCP client elicitation handlers](/agents/model-context-protocol/apis/client-api/#elicitation).
Not all MCP clients implement Legacy Elicitation. Check the client before depending on it and provide a fallback when appropriate. Agents acting as MCP clients can handle both modes through [MCP client elicitation handlers](/agents/model-context-protocol/apis/client-api/#elicitation).
:::

For more human-in-the-loop patterns, refer to [Human-in-the-loop patterns](/agents/concepts/agentic-patterns/human-in-the-loop/).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ products:

import { Render, TypeScriptExample, LinkCard } from "~/components";

Connect your agent to external [Model Context Protocol (MCP)](/agents/model-context-protocol/) servers to use their tools, resources, and prompts. This enables your agent to interact with GitHub, Slack, databases, and other services through a standardized protocol.
Connect your agent to external [Model Context Protocol (MCP)](/agents/model-context-protocol/) servers to use their tools, resources, and prompts. Agents SDK v0.18.0 uses `@modelcontextprotocol/client` and negotiates Stateless or Legacy behavior automatically.

Refer to [Migrate to MCP SDK v2](/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/) for package, type, OAuth provider, and rollout changes.

## Overview

Expand Down Expand Up @@ -390,15 +392,15 @@ for (const prompt of state.prompts) {

### Elicitation

[MCP elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation) lets a server request user input while handling another request, such as a tool call. The current stable MCP specification defines form and URL modes.
MCP servers can request user input while handling another operation. Stateless Elicitation returns `input_required` and completes through multi-round-trip requests (MRTR). Legacy Elicitation sends pushed `elicitation/create` requests. Both use form and URL modes.

Register a handler for each mode your Agent supports in `onStart()`:
Register a handler for each mode your Agent supports in `onStart()`. The same handlers serve both lanes:

<TypeScriptExample>

```ts
import { Agent } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp";
import type { ElicitRequest, ElicitResult } from "agents/mcp/client";

class MyAgent extends Agent<Env> {
onStart() {
Expand Down Expand Up @@ -429,7 +431,7 @@ The `serverId` identifies the connection that sent the request. Use it to tell t

#### Capability negotiation and hibernation

At the MCP `initialize` handshake, a connection advertises only the modes with configured handlers. A form-only handler advertises form mode. A URL-only handler advertises URL mode. A connection without handlers advertises no elicitation capability, which lets the server use its fallback.
The SDK advertises only the modes with configured handlers. Legacy connections advertise them during `initialize`. Stateless requests carry them with request capabilities. A form-only handler advertises form mode. A URL-only handler advertises URL mode. A connection without handlers advertises no elicitation capability, which lets the server use its fallback.

The SDK stores the advertised modes with each server registration. A connection restored after Durable Object hibernation can therefore advertise the same modes when it reconnects. Callback functions remain in memory and reattach when `onStart()` runs.

Expand Down Expand Up @@ -507,7 +509,7 @@ A handler returns a promise, but the response often comes from a browser. Broadc

```ts
import { Agent, callable } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp";
import type { ElicitRequest, ElicitResult } from "agents/mcp/client";

type PendingResolver = {
resolve: (result: ElicitResult) => void;
Expand Down Expand Up @@ -567,9 +569,9 @@ class MyAgent extends Agent<Env> {

The example uses a 55-second timeout because MCP SDK requests default to 60 seconds. If your client call sets a longer request timeout, adjust this timeout to finish first.

Refer to the [`mcp-client` example](https://github.com/cloudflare/agents/tree/main/examples/mcp-client) for the browser implementation. The [`mcp-elicitation` example](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) is a server that sends both modes.
Refer to the [`mcp-client` example](https://github.com/cloudflare/agents/tree/main/examples/mcp-client) for the browser implementation. [`mcp-elicitation-mrtr`](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation-mrtr) demonstrates Stateless Elicitation. [`mcp-elicitation`](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation) demonstrates Legacy Elicitation.

To send elicitation requests from an MCP server, refer to [`elicitInput`](/agents/model-context-protocol/apis/agent-api/#elicitinputoptions-context).
For server-side patterns, refer to [Stateless Elicitation](/agents/model-context-protocol/apis/handler-api/#stateless-elicitation) and [Legacy Elicitation](/agents/model-context-protocol/apis/agent-api/#legacy-elicitation).

## Managing servers

Expand Down Expand Up @@ -683,7 +685,7 @@ async addMcpServer(
callbackHost?: string;
callbackPath?: string;
agentsPrefix?: string;
client?: ClientOptions;
client?: McpClientOptions;
transport?: {
headers?: HeadersInit;
type?: "sse" | "streamable-http" | "auto";
Expand All @@ -702,7 +704,7 @@ async addMcpServer(
options?: {
id?: string;
props?: Record<string, unknown>;
client?: ClientOptions;
client?: McpClientOptions;
retry?: RetryOptions;
}
): Promise<{ id: string; state: "ready" }>
Expand All @@ -717,7 +719,7 @@ async addMcpServer(
- `callbackHost` — Host for OAuth callback URL. Only needed for OAuth-authenticated servers. If omitted, automatically derived from the incoming request or WebSocket connection URI — you typically do not need to set this unless you are using a custom domain that differs from the Worker's hostname
- `callbackPath` — Custom callback URL path that bypasses the default `/agents/{class}/{name}/callback` construction. **Required when `sendIdentityOnConnect` is `false`** to prevent leaking the instance name. When set, the callback URL becomes `{callbackHost}/{callbackPath}`. You must route this path to the agent instance via `getAgentByName`
- `agentsPrefix` — URL prefix for OAuth callback path. Default: `"agents"`. Ignored when `callbackPath` is provided
- `client` — MCP client configuration options (passed to `@modelcontextprotocol/sdk` Client constructor). By default, includes `CfWorkerJsonSchemaValidator` for validating tool parameters against JSON schemas
- `client` — The Agents-supported `McpClientOptions` subset from `@modelcontextprotocol/client`. The default validator supports JSON Schema 2020-12 and legacy draft-07 schemas in Workers
- `transport` — Transport layer configuration:
- `headers` — Custom HTTP headers for authentication
- `type` — Transport type: `"auto"` (default), `"streamable-http"`, or `"sse"`
Expand Down Expand Up @@ -867,7 +869,7 @@ export class MyAgent extends Agent {

### `configureElicitationHandlers()`

Configure handlers for server-initiated `elicitation/create` requests. Add a handler for each elicitation mode your Agent supports.
Configure handlers for Stateless Elicitation and Legacy `elicitation/create` requests. Add a handler for each elicitation mode your Agent supports.

```txt
this.mcp.configureElicitationHandlers(handlers?: {
Expand Down Expand Up @@ -896,7 +898,7 @@ Passing `undefined` clears all configured handlers.

#### Capability behavior

The client advertises only modes with configured handlers during the MCP `initialize` handshake. Handler changes apply immediately to live connections, but servers receive updated advertised modes after those connections reconnect.
The client advertises only modes with configured handlers during Legacy negotiation and on Stateless requests. Handler changes apply immediately to live connections, but servers receive updated advertised modes after those connections reconnect.

The SDK stores the handler-derived modes with each MCP server registration. Restored connections advertise those modes after Durable Object hibernation, and callbacks reattach when `onStart()` runs.

Expand All @@ -908,7 +910,7 @@ Configure handlers in `onStart()`:

```ts
import { Agent } from "agents";
import type { ElicitRequest, ElicitResult } from "agents/mcp";
import type { ElicitRequest, ElicitResult } from "agents/mcp/client";

export class MyAgent extends Agent<Env> {
onStart() {
Expand Down
Loading