CourseModel Context Protocol · Module 1: Foundations · part 6 of 83
Part 6 · Module 1: Foundations

Topic 6: How the protocol has evolved

7 min read·22 Sept 2026

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

RevisionHeadline changes
2024-11-05The 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-26A 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-18JSON-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-25OpenID 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-28The 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.

python
"""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_notes for "caffeine", and prints the negotiated revision. In auto mode the client sends server/discover; in legacy mode it runs initialize, 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-focus

    Same server, same tool, same answer, two protocol eras. MCPServer speaks both, which is why a server built with SDK v2 works with older hosts that have not moved to 2026-07-28 yet. The legacy mode also matters when you need server-initiated requests on older hosts; Module 2 and Module 4 return to it.

SituationUse thisWhy
New client code talking to servers of unknown ageClient(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 requestsmode="legacy"Forces the initialize handshake and its session-era features
New server codeMCPServer from SDK v2Answers server/discover and still accepts initialize from older clients
Any new HTTP deploymentStreamable HTTPHTTP+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.

StatusMeaning
draftHas a sponsor, undergoing informal review
in-reviewReady for formal Core Maintainer review
acceptedApproved, awaiting implementation and conformance test
rejectedDeclined by Core Maintainers
withdrawnAuthor withdrew the proposal
finalComplete with implementation and conformance
supersededReplaced by a newer SEP
dormantNo 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:

SituationUse thisWhy
You want to know what is coming before it shipsFilter pull requests in the specification repository by the in-review and accepted labelsAccepted SEPs are what the next revision will contain
A new revision has been releasedRead its changelog page, then the deprecated features registry at /specification/<revision>/deprecatedThe changelog lists every change with its SEP; the registry gives each deprecation's migration path and earliest removal date
You maintain a server or clientRead 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 changeJoin the relevant working or interest group on the MCP Contributors Discord, then comment on the SEP pull requestProposals are shaped in discussion before formal review
A feature you use is marked DeprecatedPlan 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.