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:
- Transport — how messages are carried (stdio vs HTTP)
- Primitive — what the server exposes (tools, resources, prompts, sampling)
- 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:
- https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
- Later revs (e.g. 2025-11-25) keep the same standard pair: stdio + Streamable HTTP
1. stdio (local)
| Item | Detail |
|---|---|
| Who starts the server | The host launches a subprocess |
| Wire format | Newline-delimited JSON-RPC on stdin (in) / stdout (out) |
| Logs | Use stderr only — never non-protocol data on stdout |
| Typical config | command + args (+ env, sometimes cwd) |
| Best for | Local packages (npx, uvx), filesystem, OS tools, single-user CLI/desktop |
| Network | Not required |
Host --spawn--> MCP server process
Host --stdin--> requests (initialize, tools/list, tools/call, …)
Host <--stdout- responses / notifications
Lifecycle (typical across hosts):
- Session/agent starts (or server is enabled)
- Host spawns process and connects pipes
initialize+ discovery (tools/list, …)- Process stays up; tool calls reuse the same pipes
- 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)
| Item | Detail |
|---|---|
| Who starts the server | Independent service (you or a vendor) |
| Wire format | JSON-RPC over HTTP, usually one MCP endpoint (e.g. /mcp) |
| Pattern | Client 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 config | url + optional headers / OAuth |
| Best for | Hosted APIs, multi-user, team-shared tools, cloud |
| Auth | Bearer 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)
| Item | Detail |
|---|---|
| Status | Deprecated once Streamable HTTP became the remote standard |
| Old shape | Often a POST channel for client→server plus a separate SSE stream for server→client |
| Why replaced | Two endpoints, dual connections, messier auth/CORS/session handling |
| Today | Some 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
| Feature | stdio | Streamable HTTP | Legacy HTTP+SSE |
|---|---|---|---|
| Process model | Child of the host | Independent service | Independent service |
| Config | command / args | url | url (old layout) |
| Multi-client | One process per host connection | Natural | Possible |
| Offline / local | Excellent | Needs network (or localhost) | Same |
| Auth | Env into child | Headers / OAuth | Varies |
| Spec status | Standard | Standard (remote) | Deprecated |
| Hermes | command + args | url + headers | Only if client still supports legacy |
What is not a separate transport
These are primitives (capabilities) on top of any transport:
| Primitive | Meaning |
|---|---|
| Tools | Callable actions (tools/call) — what coding agents use most |
| Resources | Readable data (docs, files, blobs) the host can fetch/attach |
| Prompts | Server-defined prompt templates |
| Sampling | Server asks the host to run an LLM mid-tool |
| Logging / notifications | Server→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
| Layer | Owner | Examples of decisions |
|---|---|---|
| MCP spec | modelcontextprotocol.io | Transports, methods, versioning |
| Host / harness | Hermes, Claude Code, Codex, Cursor, … | When to connect, reconnect, env filtering, tool names, lazy tool schemas |
Shared behavior
On connect (any transport):
- Open transport (spawn or HTTP)
initialize- Discover tools (and maybe resources/prompts)
- Expose them to the model
- On use:
tools/callon 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
| Host | Where MCP lives |
|---|---|
| Hermes | ~/.hermes/config.yaml → mcp_servers / hermes mcp add |
| Claude Code | Project/user MCP config / claude mcp add |
| Claude Desktop | claude_desktop_config.json → mcpServers |
| Codex | ~/.codex/config.toml → [mcp_servers.*] / codex mcp add |
| Cursor | MCP 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
| Situation | Prefer |
|---|---|
Local CLI package (npx, uvx, binary) | stdio |
| Needs local disk, GPU, or OS APIs | stdio (or localhost HTTP) |
| SaaS, multi-user, team-shared | Streamable HTTP |
| Old docs with dual SSE + messages URLs | Treat as legacy; migrate if you own the server |
| Too many tools bloating context | Keep standard transports; use fewer servers or host lazy tool schemas |
Security basics
| Risk | Mitigation |
|---|---|
| stdio runs arbitrary code on your machine | Only install servers you trust |
| Secrets in argv | Prefer env / headers |
| Over-broad filesystem MCP | Scope to a project directory |
| Over-privileged API tokens | Least privilege (read-only where possible) |
| Untrusted remote server | Strong 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
| Symptom | Likely cause |
|---|---|
| Tools never appear | Connect/discovery failed; host needs restart/reload |
command not found (stdio) | Host PATH differs from your interactive shell |
| Slow first stdio use | Cold npx / uvx package download |
| HTTP 401/403 | Missing or wrong Authorization / OAuth |
| HTTP works in browser tests, fails in host | Streamable HTTP vs legacy SSE mismatch; wrong path (/mcp) |
| Stateful server loses memory | Host recycled the stdio process |
Orphan node/uv processes | Host 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
- One MCP (JSON-RPC session language).
- Two standard transports: stdio (local) and Streamable HTTP (remote).
- HTTP+SSE is the deprecated remote predecessor.
- Tools / resources / prompts / sampling are capabilities, not separate protocols.
- Hosts share the model and differ on lifecycle, config format, and prompt packing.
- Hermes supports both stdio and URL-based HTTP MCP servers.
