Introduction
Transports are the communication layer of MCP. They determine how messages flow between client and server. Choosing the right transport depends on your deployment scenario: local development, remote hosting, or embedded in a web application.
Key Concepts
- StdioServerTransport: Communicates over standard input and output. Used for local servers spawned by a client process.
- StreamableHTTPServerTransport: Communicates over HTTP with streaming support. Used for remote servers accessible over the network.
- Transport selection: Local CLI tools use stdio; web-accessible servers use HTTP.
Real World Context
A developer building a personal coding assistant would use StdioServerTransport so Claude Desktop can spawn the server locally. A team deploying an MCP server as a shared service on their internal network would use StreamableHTTPServerTransport behind an Express or Fastify application so multiple clients can connect remotely.
Deep Dive
The StdioServerTransport is the simplest transport. It reads JSON-RPC messages from stdin and writes responses to stdout. This is the default choice for servers that Claude Desktop manages.
Here is the standard stdio setup:
typescriptimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new McpServer({ name: 'local-server', version: '1.0.0' }); const transport = new StdioServerTransport(); await server.connect(transport);
This is all you need for a local server. The client launches your server process and communicates through pipes.
For remote deployments, you use StreamableHTTPServerTransport which enables communication over HTTP. This transport supports streaming responses, which is important for long-running operations.
Here is an example of setting up an HTTP transport with Express. The key pattern is to create a new transport instance for each incoming POST request, which allows the SDK to manage sessions correctly:
typescriptimport express from 'express'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; const app = express(); app.use(express.json()); const server = new McpServer({ name: 'remote-server', version: '1.0.0' }); app.post('/mcp', async (req, res) => { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on('close', () => transport.close()); await server.connect(transport); await transport.handleRequest(req, res, req.body); }); app.listen(3001, () => { console.error('MCP server listening on port 3001'); });
A new StreamableHTTPServerTransport is created per POST request. The sessionIdGenerator option controls session management — passing undefined disables sessions for stateless operation. The res.on('close', ...) cleanup ensures the transport is closed when the HTTP response ends. Clients send MCP messages as HTTP POST requests to the /mcp endpoint.
When choosing a transport, consider these factors:
| Factor | Stdio | HTTP |
|---|---|---|
| Deployment | Local, single client | Remote, multiple clients |
| Security | Process isolation | Requires auth layer |
| Setup complexity | Minimal | Needs web server |
| Claude Desktop | Native support | Requires URL config |
| Scalability | Single connection | Multiple concurrent clients |
Common Pitfalls
- Using console.log with stdio: Any stdout output that is not an MCP message will corrupt the protocol. Always use
console.error()for debug logging in stdio servers. - Missing CORS for HTTP: If your HTTP server is accessed from a browser-based client, you need to configure CORS headers.
- No authentication on HTTP: Remote MCP servers should implement authentication since they are network-accessible.
Best Practices
- Start with
StdioServerTransportduring development. It is simpler to debug and test. - Add HTTP transport only when you need remote access or multiple concurrent clients.
- When using HTTP transport, always add authentication middleware before the MCP endpoint.
Summary
You learned about the two main MCP transports: StdioServerTransport for local servers and StreamableHTTPServerTransport for remote deployments. Stdio is the default for Claude Desktop integration, while HTTP enables shared network-accessible servers. Choose based on your deployment needs and security requirements.\n\nNote: An older SSE (Server-Sent Events) transport also exists in the SDK as a legacy option. For new implementations, use Streamable HTTP instead — it supports bidirectional streaming and is the recommended transport for remote deployments.
Code Examples
import express from 'express';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
const app = express();
app.use(express.json());
const server = new McpServer({ name: 'remote-server', version: '1.0.0' });
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
res.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3001);