# MCP transports compared: stdio vs SSE vs streamable HTTP

- Tool: MCP
- Difficulty: Intermediate
- Time required: 15-20 min
- Compatibility: MCP SDK 1.0+, all MCP hosts
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

```
// claude_desktop_config.json — stdio transport example
{
  "mcpServers": {
    "my-local-server": {
      "command": "node",
      "args": ["./dist/index.js"],
      "env": {
        "API_KEY": "your-key-here"
      }
    }
  }
}
```

**Expected result:** You understand that stdio uses subprocess communication via stdin/stdout with no network overhead.

### 2. 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.

```
// SSE transport server setup (legacy — avoid for new projects)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";

const app = express();
const server = new McpServer({ name: "sse-server", version: "1.0.0" });

app.get("/sse", async (req, res) => {
  const transport = new SSEServerTransport("/messages", res);
  await server.connect(transport);
});

app.post("/messages", async (req, res) => {
  // Handle incoming messages
});

app.listen(3001);
```

**Expected result:** You understand that SSE is a legacy transport being replaced by Streamable HTTP.

### 3. 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.

```
// Streamable HTTP transport with Express
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";

const server = new McpServer({
  name: "remote-server",
  version: "1.0.0"
});

server.tool("hello", "Say hello", {}, async () => ({
  content: [{ type: "text", text: "Hello from Streamable HTTP!" }]
}));

const app = createMcpExpressApp(server);
app.listen(3000, () => {
  console.error("MCP server running on http://localhost:3000");
});
```

**Expected result:** A running MCP server accessible via a single HTTP endpoint that supports both streaming and request-response patterns.

### 4. 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.

### 5. 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.

```
// Claude Desktop — stdio transport
{
  "mcpServers": {
    "local-tools": {
      "command": "node",
      "args": ["./my-server/dist/index.js"]
    }
  }
}

// Cursor — Streamable HTTP transport (.cursor/mcp.json)
{
  "mcpServers": {
    "remote-api": {
      "url": "https://my-mcp-server.railway.app/mcp"
    }
  }
}
```

**Expected result:** Your MCP host connects to servers using the appropriate transport configuration.

## Complete code example

File: `streamable-http-server.ts`

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
import { z } from "zod";

// Create the MCP server
const server = new McpServer({
  name: "demo-transport-server",
  version: "1.0.0",
});

// Register a simple tool
server.tool(
  "greet",
  "Greet a user by name",
  { name: z.string().describe("The name to greet") },
  async ({ name }) => ({
    content: [{ type: "text", text: `Hello, ${name}! Welcome to MCP.` }],
  })
);

// Register a resource
server.resource(
  "server-info",
  "info://server",
  { description: "Server metadata" },
  async () => ({
    contents: [
      {
        uri: "info://server",
        text: JSON.stringify({
          transport: "Streamable HTTP",
          version: "1.0.0",
          capabilities: ["tools", "resources"],
        }),
      },
    ],
  })
);

// Create Express app with Streamable HTTP transport
const app = createMcpExpressApp(server);

const PORT = parseInt(process.env.PORT || "3000", 10);
app.listen(PORT, () => {
  console.error(`MCP server listening on port ${PORT}`);
  console.error(`Transport: Streamable HTTP`);
  console.error(`Endpoint: http://localhost:${PORT}/mcp`);
});
```

## Common mistakes

- **Using console.log() instead of console.error() for logging** — undefined Fix: 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.
- **Choosing SSE for a new project** — undefined Fix: Use Streamable HTTP instead — SSE is being deprecated and Streamable HTTP offers better features including single-endpoint design and session management.
- **Deploying a stdio server to the cloud** — undefined Fix: Stdio requires a local subprocess — it cannot work over the network. Switch to Streamable HTTP transport for remote deployments.
- **Forgetting to set the Mcp-Session-Id header for stateful Streamable HTTP sessions** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/mcp-tutorial/mcp-transport-stdio-vs-sse-vs-http
© RapidDev — https://www.rapidevelopers.com/mcp-tutorial/mcp-transport-stdio-vs-sse-vs-http
