Back to blogs

MCP Transports

Himanshu Rai·10 min read
Published 7/11/2026

The one-line answer

There is one Model Context Protocol (MCP). It is not a pile of unrelated protocols. Differences you hear about are usually:

  1. Transport — how messages are carried (stdio vs HTTP)
  2. Primitive — what the server exposes (tools, resources, prompts, sampling)
  3. Host policy — when the client connects, reconnects, and puts tools in the LLM context

Same messages. Different pipes (or product policies).


Mental model (layers)

┌──────────────────────────────────────────────────────┐
│  What the agent can use                              │
│  tools · resources · prompts · sampling · …          │  ← primitives / capabilities
├──────────────────────────────────────────────────────┤
│  Session language (JSON-RPC 2.0)                     │
│  initialize · tools/list · tools/call · …            │  ← one protocol
├──────────────────────────────────────────────────────┤
│  How bytes move                                      │
│  stdio  |  Streamable HTTP  |  (legacy HTTP+SSE)     │  ← transports
└──────────────────────────────────────────────────────┘
  • Bottom layer: transport
  • Middle: MCP message protocol
  • Top: features a given server implements

If middle + top are MCP, switching transport does not invent a new “protocol brand” — it only changes delivery.


Official transports

Defined by the MCP specification. Clients SHOULD support stdio whenever possible. Custom transports exist but are rare.

Spec entry points:

1. stdio (local)

ItemDetail
Who starts the serverThe host launches a subprocess
Wire formatNewline-delimited JSON-RPC on stdin (in) / stdout (out)
LogsUse stderr only — never non-protocol data on stdout
Typical configcommand + args (+ env, sometimes cwd)
Best forLocal packages (npx, uvx), filesystem, OS tools, single-user CLI/desktop
NetworkNot required
Host  --spawn-->  MCP server process
Host  --stdin-->  requests (initialize, tools/list, tools/call, …)
Host  <--stdout-  responses / notifications

Lifecycle (typical across hosts):

  1. Session/agent starts (or server is enabled)
  2. Host spawns process and connects pipes
  3. initialize + discovery (tools/list, …)
  4. Process stays up; tool calls reuse the same pipes
  5. Session end / reload: close stdin, stop process

Discovery needs a live connection, so hosts usually do not wait until the first tool call to spawn.

2. Streamable HTTP (modern remote)

ItemDetail
Who starts the serverIndependent service (you or a vendor)
Wire formatJSON-RPC over HTTP, usually one MCP endpoint (e.g. /mcp)
PatternClient POSTs messages; response may be a single JSON body or an SSE stream for long/multi-message replies (GET used for some streaming patterns depending on rev/host)
Typical configurl + optional headers / OAuth
Best forHosted APIs, multi-user, team-shared tools, cloud
AuthBearer tokens, OAuth, API keys
Host  --HTTP POST /mcp-->  Remote MCP server
Host  <-- JSON body or SSE stream --

Introduced as the modern network transport around the 2025-03-26 specification family. Replaces the older dual-channel remote design (see below).

Lifecycle: server is already running. Host connects at session start (or when enabled), discovers tools, then calls tools over HTTP according to the host’s session policy. No local child process for a pure remote server.

3. Legacy: HTTP + SSE (deprecated)

ItemDetail
StatusDeprecated once Streamable HTTP became the remote standard
Old shapeOften a POST channel for client→server plus a separate SSE stream for server→client
Why replacedTwo endpoints, dual connections, messier auth/CORS/session handling
TodaySome old servers/clients still speak it for compatibility

Caution: people still say “SSE MCP.” That might mean:

  • Legacy HTTP+SSE (deprecated dual design), or
  • SSE used inside Streamable HTTP responses (modern, still valid)

Those are different generations. Prefer Streamable HTTP for anything new you control.

Comparison table

FeaturestdioStreamable HTTPLegacy HTTP+SSE
Process modelChild of the hostIndependent serviceIndependent service
Configcommand / argsurlurl (old layout)
Multi-clientOne process per host connectionNaturalPossible
Offline / localExcellentNeeds network (or localhost)Same
AuthEnv into childHeaders / OAuthVaries
Spec statusStandardStandard (remote)Deprecated
Hermescommand + argsurl + headersOnly if client still supports legacy

What is not a separate transport

These are primitives (capabilities) on top of any transport:

PrimitiveMeaning
ToolsCallable actions (tools/call) — what coding agents use most
ResourcesReadable data (docs, files, blobs) the host can fetch/attach
PromptsServer-defined prompt templates
SamplingServer asks the host to run an LLM mid-tool
Logging / notificationsServer→client events and diagnostics

A server can expose only tools, or tools + resources, over either stdio or HTTP. That is still one protocol.


Custom / non-standard transports

The spec allows custom transports if client and server agree. Experiments include:

  • WebSockets
  • Unix domain sockets
  • Gateway proxies (many backends behind one stdio or Streamable HTTP front door)

These are not the two standard bindings. For interop, prefer:

  • stdio — local
  • Streamable HTTP — remote

How hosts (harnesses) fit in

LayerOwnerExamples of decisions
MCP specmodelcontextprotocol.ioTransports, methods, versioning
Host / harnessHermes, Claude Code, Codex, Cursor, …When to connect, reconnect, env filtering, tool names, lazy tool schemas

Shared behavior

On connect (any transport):

  1. Open transport (spawn or HTTP)
  2. initialize
  3. Discover tools (and maybe resources/prompts)
  4. Expose them to the model
  5. On use: tools/call on the existing session

Where hosts differ

  • Config file format (YAML / JSON / TOML / UI)
  • Idle timeouts and reconnect
  • Whether stdio processes are recycled between sub-agents
  • Whether all tool schemas go into the LLM context immediately (eager) or only after search (lazy schemas)
  • How much of resources/prompts/sampling they implement

“Lazy MCP” almost always means lazy tool definitions in the prompt, not a third official wire protocol.

Host config cheat sheet

HostWhere MCP lives
Hermes~/.hermes/config.yamlmcp_servers / hermes mcp add
Claude CodeProject/user MCP config / claude mcp add
Claude Desktopclaude_desktop_config.jsonmcpServers
Codex~/.codex/config.toml[mcp_servers.*] / codex mcp add
CursorMCP settings / .cursor/mcp.json

Hermes: both transports

Requires the MCP Python package:

pip install mcp
# upgrade if HTTP client bits are missing:
# pip install --upgrade mcp

stdio

hermes mcp add time --command uvx --args mcp-server-time
# ~/.hermes/config.yaml
mcp_servers:
  time:
    command: uvx
    args: [mcp-server-time]
    enabled: true

Streamable HTTP

hermes mcp add company_api --url https://mcp.example.com/mcp
mcp_servers:
  company_api:
    url: "https://mcp.example.com/mcp"
    headers:
      Authorization: "Bearer sk-..."

Useful commands

hermes mcp list
hermes mcp test NAME
hermes mcp configure NAME
hermes mcp remove NAME
hermes mcp catalog
hermes mcp install <catalog-name>
hermes mcp serve          # Hermes as a server for other clients

In chat: /reload-mcp. If tool calls still fail after reload, start a new session.

Hermes notes:

  • Stdio env is filtered (safe baseline + explicit env: only).
  • Tools appear as mcp_{server}_{tool} (runtime may show double-underscore forms).
  • Connections are established at agent/tool init, not on first tool use.

Docs: https://hermes-agent.nousresearch.com/docs/user-guide/features/mcp


Same logical server, three host formats (stdio)

Hermes

mcp_servers:
  time:
    command: uvx
    args: [mcp-server-time]

Codex

[mcp_servers.time]
command = "uvx"
args = ["mcp-server-time"]

Claude-style JSON

{
  "mcpServers": {
    "time": {
      "command": "uvx",
      "args": ["mcp-server-time"]
    }
  }
}

Same process model and messages; only packaging differs.


Choosing a transport

SituationPrefer
Local CLI package (npx, uvx, binary)stdio
Needs local disk, GPU, or OS APIsstdio (or localhost HTTP)
SaaS, multi-user, team-sharedStreamable HTTP
Old docs with dual SSE + messages URLsTreat as legacy; migrate if you own the server
Too many tools bloating contextKeep standard transports; use fewer servers or host lazy tool schemas

Security basics

RiskMitigation
stdio runs arbitrary code on your machineOnly install servers you trust
Secrets in argvPrefer env / headers
Over-broad filesystem MCPScope to a project directory
Over-privileged API tokensLeast privilege (read-only where possible)
Untrusted remote serverStrong auth; consider disabling sampling if supported

Hermes specifically: stdio children do not inherit your full shell environment — only a baseline plus env: keys you set.


Troubleshooting by transport

SymptomLikely cause
Tools never appearConnect/discovery failed; host needs restart/reload
command not found (stdio)Host PATH differs from your interactive shell
Slow first stdio useCold npx / uvx package download
HTTP 401/403Missing or wrong Authorization / OAuth
HTTP works in browser tests, fails in hostStreamable HTTP vs legacy SSE mismatch; wrong path (/mcp)
Stateful server loses memoryHost recycled the stdio process
Orphan node/uv processesHost didn’t reap children on exit

Hermes:

hermes mcp test NAME
hermes mcp list

FAQ

Q: Are there multiple MCP protocols? A: No. One protocol, multiple transports and primitives.

Q: Is SSE still a thing? A: As a standalone dual-endpoint transport, it is deprecated. As a streaming mode inside Streamable HTTP, it can still appear.

Q: Does every host start stdio only when a tool is needed? A: Generally no. They connect/discover early so tools exist in the tool list. Exact idle/reconnect behavior is host-specific.

Q: Is WebSocket MCP official? A: Not as a core standard binding. Prefer stdio or Streamable HTTP for interop.

Q: Do tools vs resources mean different protocols? A: No. Same protocol; different capability surfaces.

Q: Can Hermes use both? A: Yes — command/args for stdio, url/headers for HTTP — under mcp_servers.


Quick diagrams

stdio tool call

User → Host (schemas already from tools/list)
         → tools/call via child stdin
         → server result on stdout
         → model → User

Streamable HTTP tool call

User → Host
         → POST https://host/mcp  { tools/call … }
         → JSON or SSE response
         → model → User

Takeaways

  1. One MCP (JSON-RPC session language).
  2. Two standard transports: stdio (local) and Streamable HTTP (remote).
  3. HTTP+SSE is the deprecated remote predecessor.
  4. Tools / resources / prompts / sampling are capabilities, not separate protocols.
  5. Hosts share the model and differ on lifecycle, config format, and prompt packing.
  6. Hermes supports both stdio and URL-based HTTP MCP servers.