Reading raw messages is how you debug interop problems and understand the 2026 stateless design.
Under the SDK, MCP messages are JSON-RPC 2.0: small JSON objects. A request has an id, a method (like tools/call) and params; the response carries the same id with a result or an error. A notification is a one-way message with no id and no reply.
The 2026-07-28 spec made MCP stateless. Earlier versions opened every connection with an initialize handshake and, over HTTP, tracked a session with an Mcp-Session-Id header. Both are gone. Now:
- every request carries its protocol version and the client's capabilities inside
params._meta(keys likeio.modelcontextprotocol/protocolVersion), - servers must implement
server/discover, which returns supported versions, capabilities and identity; clients may call it first, - a server that doesn't support the requested version answers with an "unsupported protocol version" error, and the client can retry with another.
Capabilities say which optional features each side supports (for example tools, resources with subscriptions, or elicitation on the client). Neither side may use a feature the other didn't declare.
Versions are dates of the last breaking change: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25, 2026-07-28. Features now move through Active, Deprecated (kept for at least twelve months) and Removed states.
Optional features beyond the core live in extensions with reverse-DNS names, such as tasks for long-running work and MCP Apps for interactive UI. SDK v2 speaks both eras: it probes with server/discover and falls back to initialize for older servers.
Example
{"jsonrpc": "2.0", "id": 7, "method": "tools/call",
"params": {
"name": "get_note",
"arguments": {"title": "groceries"},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {"elicitation": {}},
"io.modelcontextprotocol/clientInfo": {"name": "my-agent", "version": "0.3.0"}
}
}}
{"jsonrpc": "2.0", "id": 7,
"result": {
"resultType": "complete",
"content": [{"type": "text", "text": "milk, eggs"}],
"isError": false
}}Remember
- MCP messages are JSON-RPC 2.0 requests, responses and notifications
- 2026-07-28: no initialize handshake, no sessions; version and capabilities ride in _meta
- server/discover advertises versions, capabilities and identity
- Only use features the other side declared
Watch out for
- Relying on a session ID to remember per-user state between calls; current-spec clients don't send one.