Transports
JSON-RPC semantics are the same on every transport. A transport only defines framing, how metadata is attached, and how cancel/teardown work. The current spec’s standard bindings are stdio and Streamable HTTP. Older HTTP+SSE is superseded, but it still shows up as a legacy option in some clients (including some Cursor configs).
Comparison
The official Python SDK matches that: mcp.run() defaults to stdio; deploy with transport="streamable-http"; transport="sse" is compatibility only.
Stdio: local, no network overhead
A host config is usually a command plus args, not a URL:
After the process starts:
- The client writes requests to stdin
- The server writes responses to stdout
- stderr is where logs belong
Therefore:
If you run python server.py in a terminal, no output and no return is success—it is waiting for the first protocol message on stdin. Launch it from Inspector or a host; do not stare at that window for a banner.
Windows PowerShell (it should block; stop with Ctrl+C):
Unix:
Cancellation: the client sends notifications/cancelled. When the process exits, the connection is gone.
Streamable HTTP: the remote default
The client POSTs to a single endpoint (commonly /mcp). The spec requires gateway-readable headers (such as Mcp-Method, Mcp-Name, and the protocol version) so routers and rate limiters need not parse the JSON body first. Auth is ordinary HTTP: bearer tokens, API keys, custom headers. OAuth is the recommended way to obtain tokens.
Listen locally with the official SDK:
The default endpoint is http://127.0.0.1:8000/mcp. host / port / streamable_http_path are arguments to run(), not to MCPServer(...).
PowerShell probe (port only; use Inspector for a real session):
Unix:
Cancellation: the client closes that request’s response stream. The current spec does not require a long-lived session id to scale out.
Think about auth and firewalls before binding 0.0.0.0. Keep 127.0.0.1 while you learn.
Legacy SSE: awareness only
The 2025-03-26 revision replaced HTTP+SSE with Streamable HTTP. Some clients (those whose docs or UI still say SSE—Cursor is a common case) still accept an event-stream URL. That is a compatibility path, not a new API.
If you must serve a client that only speaks SSE, follow that product’s fields (sometimes url plus transport: sse) and plan a move to Streamable HTTP. Do not make SSE the primary path of a new server.
How to choose
The same tools, resources, and prompts can be developed on stdio and published by changing one run(transport=...) line. The transport changes; the primitives do not.