Skip to content
Cloudflare Docs

Transport

The Model Context Protocol (MCP) specification defines three standard transport mechanisms for communication between clients and servers:

  1. stdio, communication over standard in and standard out — designed for local MCP connections.
  2. Server-Sent Events (SSE) — Currently supported by most remote MCP clients, but is expected to be replaced by Streamable HTTP over time. It requires two endpoints: one for sending requests, another for receiving streamed responses.
  3. Streamable HTTP — New transport method introduced in March 2025. It simplifies the communication by using a single HTTP endpoint for bidirectional messaging. It is currently gaining adoption among remote MCP clients, but it is expected to become the standard transport in the future.

MCP servers built with the Agents SDK can support both remote transport methods (SSE and Streamable HTTP), with the McpAgent class automatically handling the transport configuration.

Implementing remote MCP transport

If you're building a new MCP server or upgrading an existing one on Cloudflare, we recommend supporting both remote transport methods (SSE and Streamable HTTP) concurrently to ensure compatibility with all MCP clients.

Get started quickly

You can use the "Deploy to Cloudflare" button to create a remote MCP server that automatically supports both SSE and Streamable HTTP transport methods.

Deploy to Workers

Remote MCP server (without authentication)

If you're manually configuring your MCP server, here's how to use the McpAgent class to handle both transport methods:

JavaScript
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const { pathname } = new URL(request.url);
if (pathname.startsWith('/sse')) {
return MyMcpAgent.serveSSE('/sse').fetch(request, env, ctx);
}
if (pathname.startsWith('/mcp')) {
return MyMcpAgent.serve('/mcp').fetch(request, env, ctx);
}
},
};

MCP server with authentication

If your MCP server implements authentication & authorization using the Workers OAuth Provider Library, then you can configure it to support both transport methods using the apiHandlers property.

JavaScript
export default new OAuthProvider({
apiHandlers: {
'/sse': MyMCP.serveSSE('/sse'),
'/mcp': MyMCP.serve('/mcp'),
},
// ... other OAuth configuration
})

Upgrading an existing remote MCP server

If you've already built a remote MCP server using the Cloudflare Agents SDK, make the following changes to support the new Streamable HTTP transport while maintaining compatibility with remote MCP clients using SSE:

  • Use MyMcpAgent.serveSSE('/sse') for the existing SSE transport. Previously, this would have been MyMcpAgent.mount('/sse'), which has been kept as an alias.
  • Add a new path with MyMcpAgent.serve('/mcp') to support the new Streamable HTTP transport.

If you have an MCP server with authentication/authorization using the Workers OAuth Provider, update the configuration to use the apiHandlers property, which replaces apiRoute and apiHandler.

With these few changes, your MCP server will support both transport methods, making it compatible with both existing and new clients.

Persistent transport state

When using createMcpHandler for stateless MCP servers (non-Agent-based), you can persist transport session state using the storage option. This is useful for maintaining session continuity across worker restarts or when using the same handler instance across multiple requests.

Using WorkerTransport with persistent storage

TypeScript
import { createMcpHandler, WorkerTransport } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "MyServer", version: "1.0.0" });
// Configure transport with persistent storage
const transport = new WorkerTransport({
storage: {
get: async () => {
// Retrieve state from your storage (e.g., KV, Durable Object)
return await env.MY_KV.get("transport_state", "json");
},
set: async (state) => {
// Persist state to your storage
await env.MY_KV.put("transport_state", JSON.stringify(state));
}
},
sessionIdGenerator: () => crypto.randomUUID()
});
const handler = createMcpHandler(server, { transport });
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return handler(request, env, ctx);
}
};

Storage API

The storage option accepts an object with two methods:

  • get() - Retrieves the persisted transport state. Can return Promise<TransportState | undefined> or TransportState | undefined.
  • set(state: TransportState) - Persists the current transport state. Can return Promise<void> or void.

The TransportState type includes:

  • sessionId?: string - The current session identifier
  • initialized: boolean - Whether the transport has been initialized
  • protocolVersion?: string - The MCP protocol version being used

Use cases for persistent transport state

  • Session continuity: Maintain the same session ID across worker restarts
  • Agent-based servers: When extending Agent or McpAgent, use Durable Object storage to persist transport state alongside your agent state
  • Multi-instance coordination: Share session information across multiple worker instances

Advanced handler configuration

The createMcpHandler function accepts optional configuration to customize how MCP requests are handled:

TypeScript
import { createMcpHandler, WorkerTransport } from "agents/mcp";
const handler = createMcpHandler(server, {
// Optional: Provide authentication context explicitly
authContext: {
props: { userId: "123", role: "admin" }
},
// Optional: Provide a custom transport instance
transport: new WorkerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
storage: { /* ... */ }
}),
// Optional: Customize CORS settings
corsOptions: {
allowedOrigins: ["https://example.com"],
allowedMethods: ["GET", "POST"]
},
// Optional: Specify the route path (defaults to handler for all paths)
route: "/mcp"
});

Configuration options

  • authContext: Explicitly provide authentication information (user identity, tokens, etc.) to be made available in tool handlers via getMcpAuthContext(). If not provided, the handler will automatically use authentication data from ctx.props if available.

  • transport: Supply a custom WorkerTransport instance. This is useful when you need to configure transport settings (like storage persistence) or reuse a transport across multiple requests. If not provided, a new transport is created for each handler.

  • corsOptions: Configure Cross-Origin Resource Sharing (CORS) settings for your MCP server. Useful when your MCP server needs to be accessed from web applications hosted on different domains.

  • route: Specify the URL path that this handler should respond to. If not provided, the handler will process all requests.

Testing with MCP clients

While most MCP clients have not yet adopted the new Streamable HTTP transport, you can start testing it today using mcp-remote, an adapter that lets MCP clients that otherwise only support local connections work with remote MCP servers.

Follow this guide for instructions on how to connect to your remote MCP server from Claude Desktop, Cursor, Windsurf, and other local MCP clients, using the mcp-remote local proxy.