MCP Protocol Internals · Reference

MCP over Streamable HTTP

One-page cheat sheet · spec revision 2025-11-25

The shape of it

One endpoint (e.g. http://127.0.0.1:3001/mcp). Client POSTs one JSON-RPC message per request. Server replies with either a single application/json body or a text/event-stream (SSE) of messages — its choice. Client GET opens a server-push SSE stream.

JSON-RPC message shapes

ShapeHasGets a reply?
Requestid + method (+ params)Yes — a response with same id
Notificationmethod, no idNo — server sends 202 Accepted
Responseid + result or errorIt is the reply

Required headers (client → server)

HeaderWhenValue
Acceptevery POSTapplication/json, text/event-stream (both!)
Content-Typeevery POSTapplication/json
Mcp-Session-Idevery request after init (if server minted one)the id from the initialize response
MCP-Protocol-Versionevery request after initnegotiated version, e.g. 2025-11-25

Status codes you'll meet

CodeMeaning in MCP
200Request answered — body is JSON or an SSE stream
202Notification/response accepted; empty body
400Bad request — e.g. missing session id, bad protocol version
403Origin rejected (DNS-rebinding guard) — spec MUST, not always enforced
404Session expired/unknown → client must re-initialize
405Method not allowed — e.g. server offers no GET stream / no DELETE

The handshake (always 3 messages)

C→S POST initialize {protocolVersion, capabilities, clientInfo} S→C 200 result {protocolVersion, capabilities, serverInfo, instructions} + Mcp-Session-Id C→S POST notifications/initialized → S→C 202 (empty) ── session open ──

Core methods

MethodParamsResult
initializeprotocolVersion, capabilities, clientInfoprotocolVersion, capabilities, serverInfo, instructions?
notifications/initialized(notification → 202)
tools/listcursor? (pagination)tools[], nextCursor?
tools/callname, argumentscontent[], structuredContent?, isError?
notifications/tools/list_changed(server → client; re-list)

The three server surfaces (control axis)

SurfaceControlled byListUseReturns
Toolsmodeltools/listtools/callcontent[] + structuredContent? + isError?
Resourcesapplicationresources/listresources/read (by uri)contents[] (text or blob, each with mimeType)
Promptsuserprompts/listprompts/get (name+arguments)messages[] (role + content)

Each surface also has notifications/{surface}/list_changed. Resources-only extras: resources/subscribe + updated notifications, and resources/templates/list for parameterised URIs. Prompt arguments are a flat name/description/required list — not a JSON Schema like a tool's inputSchema.

Session lifecycle controls

ActionHow
Open sessionserver sets Mcp-Session-Id on the initialize response
Use sessionclient echoes Mcp-Session-Id on every request
Server pushclient GET the endpoint → server SSE stream of requests/notifications
Resume streamclient GET with Last-Event-ID header → server replays
End sessionclient DELETE with Mcp-Session-Id (server MAY answer 405)

Source: MCP Spec 2025-11-25 — Transports, Lifecycle, Tools. Built from real traffic against the everything reference server.