MCP supports three transport protocols: stdio for local subprocess communication, SSE (Server-Sent Events) for legacy HTTP streaming, and Streamable HTTP as the newest stateless-friendly standard. Stdio is best for local tools and IDE integrations, while Streamable HTTP is the recommended choice for remote deployments with its single-endpoint design and OAuth 2.1 support.
Understanding MCP Transport Protocols
The Model Context Protocol supports three transport mechanisms for communication between hosts (like Claude Desktop or Cursor) and MCP servers. Choosing the right transport determines how your server communicates, where it can run, and what authentication options are available. This tutorial breaks down each transport so you can make the right choice for your use case.
Prerequisites
- Basic understanding of MCP architecture (hosts, clients, servers)
- Node.js 18+ or Python 3.10+ installed
- An MCP host such as Claude Desktop or Cursor installed
- Familiarity with JSON configuration files
Step-by-step guide
Understand stdio transport fundamentals
Understand stdio transport fundamentals
Stdio (standard input/output) is the simplest and most widely supported MCP transport. The host spawns your MCP server as a child process and communicates via stdin and stdout pipes. No network is involved — everything runs locally. This makes stdio the default choice for local tools, file system access, and IDE integrations. Every MCP host supports stdio, making it the most compatible option. The downside is that the server must run on the same machine as the host.
1// claude_desktop_config.json — stdio transport example2{3 "mcpServers": {4 "my-local-server": {5 "command": "node",6 "args": ["./dist/index.js"],7 "env": {8 "API_KEY": "your-key-here"9 }10 }11 }12}Expected result: You understand that stdio uses subprocess communication via stdin/stdout with no network overhead.
Review SSE transport and its deprecation status
Review SSE transport and its deprecation status
Server-Sent Events (SSE) was the first HTTP-based MCP transport. It uses two HTTP endpoints: one for the client to send messages (POST) and one for the server to stream responses (GET with SSE). While functional, SSE has limitations: it requires two connections, does not support resumability natively, and is being deprecated in favor of Streamable HTTP. If you have existing SSE servers, they will continue to work, but new projects should use Streamable HTTP instead.
1// SSE transport server setup (legacy — avoid for new projects)2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";3import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";4import express from "express";56const app = express();7const server = new McpServer({ name: "sse-server", version: "1.0.0" });89app.get("/sse", async (req, res) => {10 const transport = new SSEServerTransport("/messages", res);11 await server.connect(transport);12});1314app.post("/messages", async (req, res) => {15 // Handle incoming messages16});1718app.listen(3001);Expected result: You understand that SSE is a legacy transport being replaced by Streamable HTTP.
Set up Streamable HTTP transport for remote servers
Set up Streamable HTTP transport for remote servers
Streamable HTTP is the newest and recommended transport for remote MCP servers. It uses a single HTTP endpoint that supports both request-response and streaming patterns. Key advantages include stateless-friendly design (works behind load balancers), built-in session management via the Mcp-Session-Id header, and OAuth 2.1 authentication support. This is the transport to use when deploying MCP servers to cloud platforms like Vercel, Railway, or Render.
1// Streamable HTTP transport with Express2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";3import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";45const server = new McpServer({6 name: "remote-server",7 version: "1.0.0"8});910server.tool("hello", "Say hello", {}, async () => ({11 content: [{ type: "text", text: "Hello from Streamable HTTP!" }]12}));1314const app = createMcpExpressApp(server);15app.listen(3000, () => {16 console.error("MCP server running on http://localhost:3000");17});Expected result: A running MCP server accessible via a single HTTP endpoint that supports both streaming and request-response patterns.
Compare transports side by side
Compare transports side by side
Here is a decision matrix to help you choose. Use stdio when your server runs locally and accesses local files, databases, or system tools. Use Streamable HTTP when your server needs to be accessed remotely, shared across teams, or deployed to the cloud. Avoid SSE for new projects — it is being deprecated. Stdio has zero latency overhead but is local-only. Streamable HTTP adds network latency but enables remote access, authentication, and horizontal scaling.
Expected result: You can confidently choose the right transport for any MCP server project.
Configure your MCP host to connect via each transport
Configure your MCP host to connect via each transport
Claude Desktop and Cursor configure transports differently. For stdio, you specify a command and args in the config. For Streamable HTTP, you provide a URL. Claude Desktop stores its config at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS. Cursor uses .cursor/mcp.json in your project root or ~/.cursor/mcp.json globally. If your project involves complex multi-transport setups or you need help choosing the right architecture, the RapidDev team can help you design the optimal configuration.
1// Claude Desktop — stdio transport2{3 "mcpServers": {4 "local-tools": {5 "command": "node",6 "args": ["./my-server/dist/index.js"]7 }8 }9}1011// Cursor — Streamable HTTP transport (.cursor/mcp.json)12{13 "mcpServers": {14 "remote-api": {15 "url": "https://my-mcp-server.railway.app/mcp"16 }17 }18}Expected result: Your MCP host connects to servers using the appropriate transport configuration.
Complete working example
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";2import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";3import { z } from "zod";45// Create the MCP server6const server = new McpServer({7 name: "demo-transport-server",8 version: "1.0.0",9});1011// Register a simple tool12server.tool(13 "greet",14 "Greet a user by name",15 { name: z.string().describe("The name to greet") },16 async ({ name }) => ({17 content: [{ type: "text", text: `Hello, ${name}! Welcome to MCP.` }],18 })19);2021// Register a resource22server.resource(23 "server-info",24 "info://server",25 { description: "Server metadata" },26 async () => ({27 contents: [28 {29 uri: "info://server",30 text: JSON.stringify({31 transport: "Streamable HTTP",32 version: "1.0.0",33 capabilities: ["tools", "resources"],34 }),35 },36 ],37 })38);3940// Create Express app with Streamable HTTP transport41const app = createMcpExpressApp(server);4243const PORT = parseInt(process.env.PORT || "3000", 10);44app.listen(PORT, () => {45 console.error(`MCP server listening on port ${PORT}`);46 console.error(`Transport: Streamable HTTP`);47 console.error(`Endpoint: http://localhost:${PORT}/mcp`);48});Common mistakes
Why it's a problem: Using console.log() instead of console.error() for logging
How to avoid: Always use console.error() for logging in MCP servers. Stdout is reserved for protocol messages in stdio transport, and using console.log() will corrupt the JSON-RPC stream.
Why it's a problem: Choosing SSE for a new project
How to avoid: Use Streamable HTTP instead — SSE is being deprecated and Streamable HTTP offers better features including single-endpoint design and session management.
Why it's a problem: Deploying a stdio server to the cloud
How to avoid: Stdio requires a local subprocess — it cannot work over the network. Switch to Streamable HTTP transport for remote deployments.
Why it's a problem: Forgetting to set the Mcp-Session-Id header for stateful Streamable HTTP sessions
How to avoid: The SDK handles session management automatically. If building a custom client, capture the Mcp-Session-Id from the server response and include it in subsequent requests.
Best practices
- Default to stdio for local MCP servers — it has zero configuration overhead and maximum compatibility
- Use Streamable HTTP for any server that needs to be accessed remotely or shared across a team
- Always log to stderr, regardless of transport type, to prevent protocol corruption
- Set environment variables in the host config rather than hardcoding them in server code
- Test your server with MCP Inspector before connecting it to a host application
- Pin your @modelcontextprotocol/sdk version to avoid breaking changes across transport APIs
- Include health check endpoints in HTTP-based servers for monitoring and load balancer compatibility
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building an MCP server in TypeScript. Compare stdio, SSE, and Streamable HTTP transports. Which should I use for [local IDE tool / remote team API / cloud deployment]? Show me the server setup code and the host configuration for Claude Desktop.
Create an MCP server using Streamable HTTP transport with Express. Include a tool called [tool-name] that [description]. Show me the complete server code and the .cursor/mcp.json configuration to connect to it.
Frequently asked questions
Can I use both stdio and Streamable HTTP in the same MCP server?
Yes, but you need to run two separate instances of your server — one configured for each transport. A single server process can only use one transport at a time. You could also build your server as a library and wrap it with different transport layers.
Is SSE transport still supported in Claude Desktop?
Yes, Claude Desktop still supports SSE transport for backward compatibility. However, new servers should use Streamable HTTP, which is the recommended replacement. SSE support may be removed in a future update.
Does stdio work on Windows?
Yes, but you may need to wrap npx commands with cmd. Use {"command": "cmd", "args": ["/c", "npx", "-y", "your-server"]} in your host configuration on Windows.
What authentication does Streamable HTTP support?
Streamable HTTP supports OAuth 2.1 for authentication. The MCP specification defines a standard auth flow where the client obtains tokens from the server's authorization endpoint. Stdio transport does not need authentication since it runs locally.
Which transport has the lowest latency?
Stdio has the lowest latency because it communicates via in-process pipes with no network overhead. Streamable HTTP adds network round-trip time, which is typically 1-50ms for local servers and 50-500ms for remote servers depending on geographic distance.
Can Cursor connect to remote MCP servers?
Yes, Cursor supports Streamable HTTP transport. Add a url field to your server configuration in .cursor/mcp.json pointing to your remote server endpoint, for example https://your-server.railway.app/mcp.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation