Transport
The Model Context Protocol (MCP) specification defines two standard transport mechanisms ↗ for communication between clients and servers:
- stdio — Communication over standard in and standard out, designed for local MCP connections.
- Streamable HTTP — The standard transport method for remote MCP connections, introduced ↗ in March 2025. It uses a single HTTP endpoint for bidirectional messaging.
MCP servers built with the Agents SDK use createMcpHandler to handle Streamable HTTP transport.
Use createMcpHandler to create an MCP server that handles Streamable HTTP transport. This is the recommended approach for new MCP servers.
You can use the "Deploy to Cloudflare" button to create a remote MCP server.
Create an MCP server using createMcpHandler. View the complete example on GitHub ↗.
import { createMcpHandler } from "agents/mcp";import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { z } from "zod";
const server = new McpServer({ name: "My MCP Server", version: "1.0.0",});
server.registerTool( "hello", { description: "Returns a greeting message", inputSchema: { name: z.string().optional() }, }, async ({ name }) => { return { content: [{ text: `Hello, ${name ?? "World"}!`, type: "text" }], }; },);
export default { fetch: (request, env, ctx) => { return createMcpHandler(server)(request, env, ctx); },};import { createMcpHandler } from "agents/mcp";import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { z } from "zod";
const server = new McpServer({ name: "My MCP Server", version: "1.0.0",});
server.registerTool( "hello", { description: "Returns a greeting message", inputSchema: { name: z.string().optional() }, }, async ({ name }) => { return { content: [{ text: `Hello, ${name ?? "World"}!`, type: "text" }], }; },);
export default { fetch: (request: Request, env: Env, ctx: ExecutionContext) => { return createMcpHandler(server)(request, env, ctx); },};If your MCP server implements authentication & authorization using the Workers OAuth Provider ↗ library, use createMcpHandler with the apiRoute and apiHandler properties. View the complete example on GitHub ↗.
export default new OAuthProvider({ apiRoute: "/mcp", apiHandler: { fetch: (request, env, ctx) => { return createMcpHandler(server)(request, env, ctx); }, }, // ... other OAuth configuration});export default new OAuthProvider({ apiRoute: "/mcp", apiHandler: { fetch: (request: Request, env: Env, ctx: ExecutionContext) => { return createMcpHandler(server)(request, env, ctx); }, }, // ... other OAuth configuration});If your MCP server needs to maintain state across requests, use createMcpHandler with a WorkerTransport inside an Agent class. This allows you to persist session state in Durable Object storage and use advanced MCP features like elicitation ↗ and sampling ↗.
See Stateful MCP Servers for implementation details.
If you have an existing MCP server using the McpAgent class:
- Not using state? Replace your
McpAgentclass withMcpServerfrom@modelcontextprotocol/sdkand usecreateMcpHandler(server)in a Workerfetchhandler. - Using state? Use
createMcpHandlerwith aWorkerTransportinside an Agent class. See Stateful MCP Servers for details. - Need SSE support? Continue using
McpAgentwithserveSSE()for legacy client compatibility. See the McpAgent API reference.
You can test your MCP server using an MCP client that supports remote connections, or use mcp-remote ↗, an adapter that lets MCP clients that only support local connections work with remote MCP servers.
Follow this guide for instructions on how to connect to your remote MCP server to Claude Desktop, Cursor, Windsurf, and other MCP clients.
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-