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
| Shape | Has | Gets a reply? |
| Request | id + method (+ params) | Yes — a response with same id |
| Notification | method, no id | No — server sends 202 Accepted |
| Response | id + result or error | It is the reply |
Required headers (client → server)
| Header | When | Value |
Accept | every POST | application/json, text/event-stream (both!) |
Content-Type | every POST | application/json |
Mcp-Session-Id | every request after init (if server minted one) | the id from the initialize response |
MCP-Protocol-Version | every request after init | negotiated version, e.g. 2025-11-25 |
Status codes you'll meet
| Code | Meaning in MCP |
200 | Request answered — body is JSON or an SSE stream |
202 | Notification/response accepted; empty body |
400 | Bad request — e.g. missing session id, bad protocol version |
403 | Origin rejected (DNS-rebinding guard) — spec MUST, not always enforced |
404 | Session expired/unknown → client must re-initialize |
405 | Method 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
| Method | Params | Result |
initialize | protocolVersion, capabilities, clientInfo | protocolVersion, capabilities, serverInfo, instructions? |
notifications/initialized | — | (notification → 202) |
tools/list | cursor? (pagination) | tools[], nextCursor? |
tools/call | name, arguments | content[], structuredContent?, isError? |
notifications/tools/list_changed | — | (server → client; re-list) |
The three server surfaces (control axis)
| Surface | Controlled by | List | Use | Returns |
| Tools | model | tools/list | tools/call | content[] + structuredContent? + isError? |
| Resources | application | resources/list | resources/read (by uri) | contents[] (text or blob, each with mimeType) |
| Prompts | user | prompts/list | prompts/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
| Action | How |
| Open session | server sets Mcp-Session-Id on the initialize response |
| Use session | client echoes Mcp-Session-Id on every request |
| Server push | client GET the endpoint → server SSE stream of requests/notifications |
| Resume stream | client GET with Last-Event-ID header → server replays |
| End session | client 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.