MCP revisions use date strings such as `2025-11-25`. The date identifies the most recent backwards-incompatible protocol change, not a package release or marketing version. Client and server negotiate one revision during initialization and must interpret the rest of that connection under the agreed contract.
As of July 12, 2026, `2025-11-25` is the current stable protocol revision. A larger `2026-07-28` release candidate is available for validation but is not yet the final stable specification. Production systems should pin supported revisions and adopt draft behavior only in isolated compatibility testing.
Migration is broader than changing the initialize message. Schemas, capabilities, HTTP headers, task semantics, authorization discovery, SDK behavior, and extension support may all move together.
| Track | Use | Engineering Rule |
|---|---|---|
| Current stable | Production interoperability | Implement the published specification and pin contract tests. |
| Release candidate | Pre-release validation | Use isolated environments and expect final corrections. |
| Final historical revision | Existing compatibility | Support only while real clients or servers require it. |
| Unversioned custom behavior | Private experiments | Place behind an explicit extension or remove it. |
Protocol revision strings follow `YYYY-MM-DD`. A backwards-compatible clarification can update documentation without changing the revision, while an incompatible wire change requires a new date. Do not compare revision strings to SDK package versions; they describe different contracts.
Store protocol support as an explicit set, not a vague minimum. A newer date does not guarantee that an implementation can safely reinterpret every older message, especially when experimental features or extensions changed shape.
The client proposes a protocol revision in `initialize`. If the server supports it, the server returns that revision. If not, the server returns another revision it supports. The client then decides whether the returned value is acceptable or terminates the connection.
Normal capability discovery must wait until initialization succeeds. For Streamable HTTP, the client also sends the negotiated revision through `MCP-Protocol-Version` on later requests. A proxy that drops the header can create failures that look like schema or authorization bugs.
Stable specifications are the interoperability target. Drafts and release candidates exist so SDK and host maintainers can validate major changes before final publication. They are useful for test environments but should not silently become the production default.
The `2026-07-28` release candidate proposes a stateless core, a formal extensions mechanism, revised long-running task support, authorization changes, and explicit deprecation policy. These are important migration signals, but code should follow the final published schema before claiming production conformance.
Keep draft adapters behind a feature flag and label their traces, fixtures, and test reports clearly. Remove or update them when the final revision ships rather than carrying an unofficial hybrid forward.
The stable `2025-11-25` revision added URL-mode elicitation, sampling with tools, Client ID Metadata Documents, richer implementation metadata, and experimental core tasks. It also made implicit sampling context less central by soft-deprecating the broad `includeContext` values.
A server can use these features only when the other party advertised the matching capability. An older client may still initialize successfully while lacking URL elicitation, sampling tools, or tasks, so revision support never replaces capability negotiation.
The experimental core task API in `2025-11-25` and the newer Tasks extension are not wire-compatible. Core tasks include `tasks/result` and `tasks/list`; the extension removes those methods, adds `tasks/update`, changes result discrimination, and negotiates under `io.modelcontextprotocol/tasks`.
A bridge can support both surfaces, but it must choose from the negotiated protocol and extension capabilities. Never send both task capability shapes or guess the contract from method availability after a failure.
Test task creation, polling, input-required state, cancellation, terminal results, expired handles, and authorization binding independently for each supported surface.
Build a matrix with client, server, SDK, transport, protocol revision, capability set, extension set, and authorization profile. Run it against the combinations that customers actually use, including one expected incompatibility so the failure message is tested.
Compare more than parsing. Verify discovery, tool and resource schemas, sampling and elicitation flows, cancellation, error codes, HTTP headers, trace fields, and user consent. A request that returns JSON can still behave under the wrong protocol contract.
A deprecation plan names the old contract, affected users, telemetry signal, replacement, warning period, removal date, and rollback. Avoid permanent dual support when no real client still uses the older path.
Telemetry should count negotiated revisions and capability use without storing sensitive request content. Contact users of old revisions before removal, then fail with a precise compatibility message instead of a generic internal error.
MCP revision identifiers use `YYYY-MM-DD` and change for backward-incompatible protocol updates. The current revision is `2025-11-25`; older finalized revisions remain relevant for compatibility. Draft documents are design work, not a contract a production peer can assume.
During initialize, the client sends its latest supported version and may support older revisions. The server returns the version it wants to use, which may differ. If the client cannot support that selection, it must disconnect. Capability negotiation then determines optional behavior within the agreed revision; revision support alone does not imply tasks, sampling, roots, or list-change notifications.
A migration suite should pair old and new clients and servers, compare schemas, and test missing fields, changed defaults, removed methods, transport headers, and extension fallbacks. Record the negotiated version in traces and error reports so failures are reproducible instead of attributed vaguely to “MCP compatibility.”
This simulation selects one mutually supported revision and rejects an unsupported server response.
CLIENT_SUPPORT = ["2025-11-25", "2025-06-18"]
SERVER_SUPPORT = {"2025-11-25", "2025-03-26"}
def negotiate(client_preference: list[str], server_support: set[str]) -> str:
for revision in client_preference:
if revision in server_support:
return revision
raise RuntimeError("No mutually supported MCP revision")
selected = negotiate(CLIENT_SUPPORT, SERVER_SUPPORT)
print("Selected:", selected)
print("HTTP header: MCP-Protocol-Version:", selected)
Selected: 2025-11-25
HTTP header: MCP-Protocol-Version: 2025-11-25
Preference order is explicit; the function does not assume that every newer server supports every older revision.
A release gate checks that every required compatibility lane passed before a protocol change ships.
lanes = {
"stable_stdio": True,
"stable_http": True,
"old_client_compatibility": True,
"release_candidate_shadow": False,
}
required = {"stable_stdio", "stable_http", "old_client_compatibility"}
failed = sorted(name for name in required if not lanes[name])
print("SHIP" if not failed else "BLOCK")
for name in failed:
print("- Failed:", name)
SHIP
The draft shadow lane is informative until the team deliberately makes it a release requirement.
0 of 2 checked
Try this next
0 of 3 completed
No. Support the revisions real clients require, measure use, publish a migration window, and remove obsolete adapters when the support commitment ends.
Not as of July 12, 2026. It is a release candidate intended for validation before the planned final publication date.
Explore 500+ free tutorials across 20+ languages and frameworks.