Migrate to MCP SDK v2·续
Do not remove the legacy route as soon as the stateless implementation exists. Serve both lanes while clients migrate and existing sessions drain.
Run stateless and legacy lanes together
A single URL can route stateless requests to SDK v2 and legacy requests to the existing sessionful server.
import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
const stateless = createMcpHandler(createStatelessServer, {
route: "/mcp",
legacy: "reject",
});
const legacy = MyMcpAgent.serve("/mcp");
export default {
async fetch(request, env, ctx) {
if (await isLegacyRequest(request)) {
return legacy.fetch(request, env, ctx);
}
return stateless(request, env, ctx);
},
};
import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
const stateless = createMcpHandler(createStatelessServer, {
route: "/mcp",
legacy: "reject",
});
const legacy = MyMcpAgent.serve("/mcp");
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
if (await isLegacyRequest(request)) {
return legacy.fetch(request, env, ctx);
}
return stateless(request, env, ctx);
},
} satisfies ExportedHandler<Env>;
Keep legacy: "reject" on the stateless handler. Otherwise, its legacy compatibility lane consumes requests before the sessionful route receives them.
Deploy both routes before moving clients. Monitor the legacy lane and let existing sessions drain. Remove the legacy route and its protocol-only Durable Object binding only after no clients depend on them. Handle Durable Object migration configuration as a separate deployment step.
Update MCP clients
Agents now uses @modelcontextprotocol/client internally. Existing addMcpServer calls negotiate the protocol era automatically.
Servers on the stateless path use server/discover. Agents falls back to initialize for legacy Streamable HTTP, SSE, and RPC servers.
The client API includes these changes:
callTool(params, options)is the preferred signature.callTool(params, resultSchema, options)remains available but is deprecated.- MCP client types now come from
@modelcontextprotocol/client. - Required stateless HTTP headers are handled by the SDK.
- List changes use stateless subscriptions or legacy notifications based on the negotiated lane.
Configure elicitation for stateless requests
Tools, prompts, and resources on the stateless path can return input_required through multi-round-trip requests (MRTR). The SDK calls the configured elicitation handler and retries the original operation. Your original callTool, getPrompt, or readResource promise remains pending.
Each retry contains responses for the immediately preceding input round, not every earlier response. The client also echoes the latest opaque requestState. Seal trusted intermediate values needed by later rounds into integrity-protected requestState; do not expect inputResponses to accumulate across rounds.
Refer to the stateless elicitation example ↗︎ for a two-round tool flow.
export class MyAgent extends Agent {
onStart() {
this.mcp.configureElicitationHandlers({
form: async (request, serverId, signal) => {
return collectInput(request, serverId, signal);
},
url: async (request, serverId, signal) => {
return openExternalFlow(request, serverId, signal);
},
});
}
}
export class MyAgent extends Agent<Env> {
onStart() {
this.mcp.configureElicitationHandlers({
form: async (request, serverId, signal) => {
return collectInput(request, serverId, signal);
},
url: async (request, serverId, signal) => {
return openExternalFlow(request, serverId, signal);
},
});
}
}
Handlers and in-flight calls remain in memory. Hibernation, isolate restart, transport loss, or connection reconstruction rejects an active interactive call. Retry the operation after the connection recovers.
Treat manually handled requestState as untrusted input. Bind it to the authenticated user and operation, protect its integrity, and set a short expiry.
Update custom OAuth providers
A custom AgentMcpOAuthProvider must implement the v2 OAuthClientProvider contract:
- Import OAuth types from
@modelcontextprotocol/client. - Store
StoredOAuthClientInformationandStoredOAuthTokens. - Preserve the SDK issuer stamp on credentials.
- Persist
OAuthDiscoveryStateacross browser redirects. - Accept
"discovery"ininvalidateCredentials. - Keep credentials separate when authorization issuers differ.
SDK v2 validates OAuth metadata issuers by default. A trusted legacy server with known mismatched metadata can use skipIssuerMetadataValidation: true. This weakens OAuth mix-up protection and should not be a general fallback.
Review protocol differences
MCP's stateless model changes the transport and lifecycle:
| Area | Previous behavior | New behavior |
|---|---|---|
| Startup | initialize handshake |
No handshake. server/discover is optional for clients |
| Request metadata | Connection-scoped negotiation | Version, client capabilities, and identity metadata on each request |
| Sessions | Optional Mcp-Session-Id |
No protocol session |
| Server input requests | Server sends JSON-RPC requests | Server returns input_required. Client retries the original operation |
| Change notifications | Standalone GET stream and list-change notifications | subscriptions/listen POST with an SSE response |
| Stream recovery | Last-Event-ID can resume configured streams |
Listen streams reopen after failure. There is no Last-Event-ID replay. |
Custom transports, proxies, and gateways must preserve the draft request headers:
MCP-Protocol-VersionMcp-MethodMcp-Namefor tool, prompt, and resource operations- Declared
Mcp-Param-*tool headers
The exact SDK version used by Agents implements the MCP 2026-07-28 revision. It makes clientInfo optional and places server identity in result _meta. Use high-level SDK APIs and update MCP packages with the Agents release that supports each protocol revision. Raw stateless results must include resultType.
The draft deprecates Roots, Sampling, Logging, the old HTTP+SSE transport, and Dynamic Client Registration. The types remain available during the deprecation window for legacy compatibility. The published experimental task methods become the io.modelcontextprotocol/tasks extension. Agents SDK v0.20.0 does not add that extension.
Integration compatibility
The following integrations retain SDK v1 server output in this release:
- Current Code Mode
codeMcpServerandopenApiMcpServerhelpers - The server-side
withX402helper - Existing OpenAI Apps examples that import an SDK v1
McpServer
Until these integrations produce SDK v2 servers, isolate their output behind a temporary createLegacyMcpHandler route. The Code Mode MCP connector and withX402Client accept either client generation.
Plan the rollout
- Classify each endpoint by its sessionful dependencies.
- Pin the MCP SDK versions required by the Agents release.
- Move each server definition that can be stateless to an SDK v2 factory.
- Add stateless handlers beside endpoints that still need a temporary legacy lane.
- Route stateless and legacy requests with
isLegacyRequest(). - Test stateless and legacy clients independently.
- Test required HTTP headers through every proxy and gateway.
- Test OAuth after a clean login and after Durable Object hibernation.
- Test cancellation, multiple input rounds, and transport loss.
- Verify valid Origins and reject invalid Origins with
403. - Remove legacy routes only after existing sessions drain.
Stored HTTP session IDs from Agents releases before v0.20.0 do not include the negotiated protocol version. The upgraded client discards those IDs and reconnects instead of sending an unsafe resumed request. Existing in-flight work tied to an old remote session does not resume.
For API details, refer to createMcpHandler and McpClient.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/#page","headline":"Migrate to MCP SDK v2","description":"Migrate Agents SDK MCP servers and clients to the split MCP TypeScript SDK v2 packages while retaining legacy compatibility.","url":"https://developers.cloudflare.com/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/og.png?v=6d00c9de2a63b5f1","dateModified":"2026-07-28","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Implement OAuth authentication flows in Cloudflare Agents to connect to protected MCP servers. title: Handle OAuth with MCP servers image: https://developers.cloudflare.com/agents/model-context-protocol/guides/oauth-mcp-client/og.png?v=0c93b7fceb3b8d12
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.
Docs