Transport Types: HTTP, stdio, WebSocket, SSE
- Choose the right transport type for any MCP server — HTTP for remote services, stdio for local processes, WebSocket for persistent bidirectional connections
- Configure stdio servers correctly using the -- separator, CLAUDE_PROJECT_DIR, and the roots/list protocol for dynamic project directory awareness
- Diagnose the url-but-no-type misconfiguration error and tune per-server timeouts for slow-starting local processes
Why the transport choice shapes everything
When you add an MCP server, you are not just choosing where it runs. You are choosing how Claude Code communicates with it, what authentication options are available, whether it reconnects automatically on failure, and whether it can push events into your session. Transport type determines all of this. Getting it wrong produces errors that look like authentication failures or connection timeouts — making it one of the first things to get right.
HTTP: the standard for remote services
HTTP — officially called streamable-http in the MCP specification — is the recommended transport for any cloud service or hosted API. You configure it with type: "http" and a URL:
claude mcp add --transport http notion https://mcp.notion.com/mcp
HTTP supports OAuth 2.0 authentication, static Bearer tokens via headers, and dynamic token generation via headersHelper. It reconnects automatically on disconnection with exponential backoff — up to five attempts, starting at one second and doubling each time. After five failed attempts, the server is marked failed and you can retry manually from /mcp.
One common misconfiguration: copying a server's JSON entry from documentation but omitting the type field. Claude Code reads an entry with a url but no type as a stdio server and skips it with the error: MCP server has a "url" but no "type"; add "type": "http". This was changed in v2.1.202 — earlier versions showed the less helpful message command: expected string, received undefined.
stdio: local processes with direct system access
stdio servers run as child processes that Claude Code spawns on your machine. They communicate over standard input and output, giving them direct access to your local filesystem, databases, and any tool installed locally — without requiring a network.
The critical syntax detail for stdio servers is the -- separator. Everything before -- is parsed as Claude's own flags (--transport, --env, --scope). Everything after -- is passed unchanged as the command that starts the server:
claude mcp add --env AIRTABLE_API_KEY=your-key --transport stdio airtable -- npx -y airtable-mcp-server
Without the --, Claude Code tries to parse the server's flags as its own and fails with a confusing error. This is the most common stdio setup mistake.
CLAUDE_PROJECT_DIR is an environment variable Claude Code sets in every spawned server's environment, pointing to the stable project root. Unlike the working directory — which can shift when you add or remove directories mid-session — CLAUDE_PROJECT_DIR never changes. Your server reads it with process.env.CLAUDE_PROJECT_DIR (Node) or os.environ["CLAUDE_PROJECT_DIR"] (Python) to resolve project-relative paths reliably.
For servers that need to track which directories are in scope as the session evolves, implement the roots/list request. Claude Code answers it with the session's launch directory plus every additional directory granted with --add-dir or /add-dir. Claude Code also sends notifications/roots/list_changed whenever that set changes, so your server can update its allowed paths dynamically.
Note that stdio servers do not reconnect automatically. If the server process exits, it stays disconnected until you restart the session or reconnect manually from the /mcp panel.
WebSocket: persistent bidirectional connections
WebSocket servers hold a persistent, bidirectional connection between Claude Code and the server, which makes them suited for MCP servers that need to push events into your session unprompted — without polling. Unlike HTTP servers, they are not configured with the --transport flag; you must use claude mcp add-json or edit the configuration file directly:
claude mcp add-json events-server '{"type":"ws","url":"wss://mcp.example.com/socket","headers":{"Authorization":"Bearer YOUR_TOKEN"}}'
WebSocket authentication is header-only — there is no OAuth support. Pass a static token in headers, or use headersHelper to generate one at connection time. For servers that only respond to requests rather than pushing events unprompted, prefer HTTP, which has fuller authentication support and the --transport flag shorthand.
SSE: the legacy option
Server-Sent Events (SSE) is the original remote transport for MCP servers. It is deprecated — Anthropic recommends HTTP for all new remote servers — but still fully supported for existing configurations. The syntax mirrors HTTP:
claude mcp add --transport sse asana https://mcp.asana.com/sse
If you encounter an SSE server in the wild, it works. For any new server you are setting up or building, use HTTP instead.
Timeouts, idle limits, and reconnection behavior
Two timeout settings matter for MCP performance. The MCP_TIMEOUT environment variable controls how long Claude Code waits for a server to start — useful for stdio servers whose first run is slow because npx must download the package:
MCP_TIMEOUT=60000 claude
The per-server timeout field in your .mcp.json entry sets a hard wall-clock limit on each tool call. Values below 1000 milliseconds are ignored. When set, this value also acts as a floor on the idle timeout — Claude Code never abandons a tool call for idleness before the per-server timeout has elapsed.
The idle timeout is separate: if a server sends no response and no progress notification for the idle window (5 minutes for HTTP/SSE/WebSocket, 30 minutes for stdio), the tool call aborts with an error. Set CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT in milliseconds to change the window, or to 0 to disable the check entirely for long-running operations.
- HTTP (streamable-http) is the recommended transport for any remote service — it supports OAuth, header tokens, headersHelper, and automatic reconnection with exponential backoff
- stdio servers run as local child processes; the -- separator is required to distinguish Claude's own flags from the server command, and omitting it is the most common stdio setup mistake
- CLAUDE_PROJECT_DIR gives stdio servers a stable reference to the project root that persists when working directories change mid-session
- WebSocket requires claude mcp add-json rather than --transport and only supports header-based authentication — use HTTP instead when your server only responds to requests
- MCP_TIMEOUT controls server startup wait time; the per-server timeout field sets a hard wall-clock limit per tool call and acts as a floor on the idle timeout