Topic 6: How the protocol has evolved
MCP has had five specification revisions in under two years. Each revision is named by the date it was finalised, and that date string is the protocol version that clients and servers exchange (you saw 2026-07-28 in every message in Part D). Many servers in the wild still speak older revisions, so knowing what changed when is practical, not trivia.
E.1 The five revisions
| Revision | Headline changes |
|---|---|
| 2024-11-05 | The first public specification, released with MCP's announcement in November 2024. JSON-RPC 2.0 messages; an initialize handshake that opened a stateful connection; two transports, stdio and HTTP+SSE (a POST endpoint plus a separate Server-Sent Events stream for server messages); server primitives tools, resources, and prompts; client features sampling and roots; utilities such as logging, progress, cancellation, ping, and argument completion. |
| 2025-03-26 | A comprehensive authorization framework based on OAuth 2.1. Streamable HTTP replaces HTTP+SSE: one endpoint that answers each POST with either plain JSON or an SSE stream. JSON-RPC batching added. Tool annotations (hints such as read-only or destructive). Audio content, a completions capability, and a message field on progress notifications. |
| 2025-06-18 | JSON-RPC batching removed again. Structured tool output (structuredContent and outputSchema). MCP servers classified as OAuth resource servers, with protected resource metadata that points clients to a separate authorization server, and clients required to use resource indicators (RFC 8707) so a token is bound to one server. Elicitation added. Resource links in tool results. The MCP-Protocol-Version header required on HTTP requests after the handshake. A title field for human-friendly names. |
| 2025-11-25 | OpenID Connect discovery for authorization servers; icons on tools, resources, and prompts; incremental scope consent through WWW-Authenticate; tool naming guidance; URL-mode elicitation; tool calling inside sampling; Client ID Metadata Documents as the recommended client registration; experimental tasks for durable, pollable requests (SEP-1686); JSON Schema 2020-12 as the default dialect; input validation errors returned as tool errors so models can self-correct (SEP-1303); formal governance. |
| 2026-07-28 | The stateless redesign. The initialize handshake is removed and every request carries its protocol version and client capabilities in _meta (SEP-2575). Servers must implement server/discover to advertise versions, capabilities, and identity (SEP-2575). Protocol-level sessions and the Mcp-Session-Id header are removed; servers that need state across calls return explicit handles as ordinary tool arguments (SEP-2567). Multi Round-Trip Requests replace server-initiated requests such as elicitation and sampling (SEP-2322), and every result gets a resultType. Tasks move out of the core into an official extension (SEP-2663). subscriptions/listen replaces the HTTP GET stream and resources/subscribe. ping and logging/setLevel are removed. Mcp-Method and Mcp-Name headers become required on Streamable HTTP (SEP-2243). Cacheable list results with ttlMs and cacheScope (SEP-2549). Roots, sampling, and logging deprecated (SEP-2577); Dynamic Client Registration deprecated in favour of Client ID Metadata Documents; a formal feature lifecycle with a minimum twelve-month deprecation window (SEP-2596). |
Source: the official changelog pages at modelcontextprotocol.io/specification/<revision>/changelog, one per revision, each listing changes since the previous one.
E.2 The major shifts, and why they happened
Five shifts explain most of the history. Each one solved a problem that real deployments ran into.
1. Transport replacement (2025-03-26). HTTP+SSE needed two endpoints and a long-lived SSE connection per client, which is awkward behind load balancers and serverless platforms. Streamable HTTP uses one endpoint; each POST gets either a plain JSON reply or an SSE stream when the server has more to say. Our curl calls in Part D received plain application/json replies from that single /mcp endpoint. HTTP+SSE is now formally Deprecated (SEP-2596) and should not be used for anything new.
2. The OAuth model (2025-03-26, reshaped 2025-06-18). The first authorization design let an MCP server act as its own authorization server. The 2025-06-18 revision separated the roles: the MCP server is an OAuth resource server that only validates tokens, and a separate authorization server (your identity provider) issues them. Resource indicators bind each token to one server so a malicious server cannot replay it elsewhere. Module 7 builds exactly this.
3. Elicitation (2025-06-18, extended 2025-11-25). Servers sometimes need a missing detail from the human, not the model. Elicitation gave them a structured way to ask, first through forms and later through URLs for sensitive flows such as signing in to a third-party service.
4. Structured output (2025-06-18). Tool results had been text blocks meant for a model. Structured output added a JSON copy that matches a declared output schema, so application code can use results without parsing prose. You saw both halves in the tools/call response.
5. The stateless redesign (2026-07-28). Earlier revisions opened a stateful connection with initialize and, over HTTP, pinned it with a session id. That made horizontal scaling hard: a request had to reach the replica that held its session. In 2026-07-28 every request is self-contained. Discovery became an optional single call, server/discover; server-initiated requests became Multi Round-Trip Requests, where the server returns input_required and the client retries; and state that must survive between calls is carried explicitly as handles in tool arguments. Module 2 and Module 9 build on this.
You can see both eras from one client. The Python SDK's Client defaults to mode="auto": it probes with server/discover and falls back to the initialize handshake if the server is older. mode="legacy" forces the old handshake.
"""Module 1: one server, two protocol eras.
Run: PYTHONPATH=. python examples/m01_versions.py
"""
import sys
import anyio
from mcp import Client
sys.path.insert(0, "examples")
from m01_first_server import mcp # noqa: E402
async def main() -> None:
for mode in ["auto", "legacy"]:
async with Client(mcp, mode=mode) as client:
result = await client.call_tool("search_notes", {"query": "caffeine", "limit": 1})
top = result.structured_content["result"][0]["note_id"]
print(f"mode={mode:<6} negotiated {client.protocol_version} top hit {top}")
if __name__ == "__main__":
anyio.run(main)Code explained
- In simple words: the same conversation held in two dialects, to show the server understands both.
- What happens: for each mode the client connects in memory to the Part C server, calls
search_notesfor "caffeine", and prints the negotiated revision. Inautomode the client sendsserver/discover; inlegacymode it runsinitialize, the handshake every pre-2026 client uses. - Comes out: real output:text
mode=auto negotiated 2026-07-28 top hit coffee-and-focus mode=legacy negotiated 2025-11-25 top hit coffee-and-focusSame server, same tool, same answer, two protocol eras.
MCPServerspeaks both, which is why a server built with SDK v2 works with older hosts that have not moved to 2026-07-28 yet. Thelegacymode also matters when you need server-initiated requests on older hosts; Module 2 and Module 4 return to it.
| Situation | Use this | Why |
|---|---|---|
| New client code talking to servers of unknown age | Client(target) (default mode="auto") | One probe; falls back automatically to the handshake for older servers |
| A host that must match a pre-2026 server's behaviour exactly, or needs server-initiated requests | mode="legacy" | Forces the initialize handshake and its session-era features |
| New server code | MCPServer from SDK v2 | Answers server/discover and still accepts initialize from older clients |
| Any new HTTP deployment | Streamable HTTP | HTTP+SSE is Deprecated; do not build on it |
E.3 Following Specification Enhancement Proposals
The protocol changes through Specification Enhancement Proposals (SEPs). A SEP is a design document for a significant change: a new feature, a breaking change, a governance change, or anything controversial. Small fixes and typo corrections are ordinary pull requests instead. The SEP numbers in the revision table above (SEP-2575, SEP-2577, and so on) are how you trace any change back to its design and discussion.
Since the 2026-07-28 revision, the workflow is pull-request based (SEP-1850): an author adds a Markdown file to the seps/ directory of the specification repository (github.com/modelcontextprotocol/modelcontextprotocol), and the SEP takes its number from the pull request. A sponsor (a Core Maintainer or Maintainer) champions the proposal and manages its status. Core Maintainers review SEPs every two weeks. A SEP reaches Final only once a reference implementation exists and, for protocol behaviour you can observe, a conformance test is merged.
| Status | Meaning |
|---|---|
draft | Has a sponsor, undergoing informal review |
in-review | Ready for formal Core Maintainer review |
accepted | Approved, awaiting implementation and conformance test |
rejected | Declined by Core Maintainers |
withdrawn | Author withdrew the proposal |
final | Complete with implementation and conformance |
superseded | Replaced by a newer SEP |
dormant | No sponsor found within 6 months; can be revived |
There are four SEP types: Standards Track (protocol features), Extensions Track (optional extensions such as tasks), Informational, and Process.
A practical routine for staying current, which takes about fifteen minutes a month:
| Situation | Use this | Why |
|---|---|---|
| You want to know what is coming before it ships | Filter pull requests in the specification repository by the in-review and accepted labels | Accepted SEPs are what the next revision will contain |
| A new revision has been released | Read its changelog page, then the deprecated features registry at /specification/<revision>/deprecated | The changelog lists every change with its SEP; the registry gives each deprecation's migration path and earliest removal date |
| You maintain a server or client | Read your SDK's release notes and migration guide (for Python, "What's new" and the migration guide on the SDK docs site) | The SDK tells you which revisions it speaks and which APIs changed |
| You want to influence a change | Join the relevant working or interest group on the MCP Contributors Discord, then comment on the SEP pull request | Proposals are shaped in discussion before formal review |
| A feature you use is marked Deprecated | Plan the migration within the window (at least twelve months under SEP-2596) | Deprecated features keep working until the earliest removal date, then may disappear in any later revision |
This course is pinned to mcp==2.2.0 and revision 2026-07-28. When the next revision lands, Modules 2, 4, 5, and 9 are the ones most likely to need a review; the ideas in this module and in Modules 3, 8, and 10 change slowly.