Skip to content

Commit cb9e260

Browse files
committed
Rename SUPPORTED_PROTOCOL_VERSIONS to HANDSHAKE_PROTOCOL_VERSIONS; consolidate header constant
- HANDSHAKE_PROTOCOL_VERSIONS names what the constant actually holds (versions reachable via the initialize handshake); SUPPORTED_PROTOCOL_VERSIONS survives as a deprecated union of HANDSHAKE + MODERN for v1.x compatibility - The three handshake-ceiling call sites (initialize offer, server negotiate fallback, for_loop seed) now read HANDSHAKE_PROTOCOL_VERSIONS[-1] instead of LATEST_PROTOCOL_VERSION - Era-routing in the streamable-HTTP manager reads HANDSHAKE_PROTOCOL_VERSIONS (interim; the body-primary classifier is the structural fix) - mcp-protocol-version header constant: three duplicate definitions collapsed to the single MCP_PROTOCOL_VERSION_HEADER in shared/inbound; client and server importers point at the canonical module - migration.md documents the SUPPORTED deprecation
1 parent a527142 commit cb9e260

16 files changed

Lines changed: 54 additions & 38 deletions

‎docs/migration.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,10 @@ async def my_tool(ctx: Context) -> str: ...
764764
async def my_tool(ctx: Context[MyLifespanState]) -> str: ...
765765
```
766766

767+
### Version constants
768+
769+
`SUPPORTED_PROTOCOL_VERSIONS` is deprecated — it's now the union of `HANDSHAKE_PROTOCOL_VERSIONS` (initialize-handshake versions) and `MODERN_PROTOCOL_VERSIONS` (per-request-envelope versions). If you were using it to mean "versions the initialize handshake accepts", switch to `HANDSHAKE_PROTOCOL_VERSIONS`.
770+
767771
### `ProgressContext` and `progress()` context manager removed
768772

769773
The `mcp.shared.progress` module (`ProgressContext`, `Progress`, and the `progress()` context manager) has been removed. This module had no real-world adoption — all users send progress notifications via `Context.report_progress()` or `session.send_progress_notification()` directly.

‎src/mcp/client/auth/oauth2.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
validate_authorization_response_iss,
4141
validate_metadata_issuer,
4242
)
43-
from mcp.client.streamable_http import MCP_PROTOCOL_VERSION
4443
from mcp.shared.auth import (
4544
AuthorizationCodeResult,
4645
OAuthClientInformationFull,
@@ -54,6 +53,7 @@
5453
check_resource_allowed,
5554
resource_url_from_server_url,
5655
)
56+
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
5757
from mcp.shared.version import is_version_at_least
5858

5959
logger = logging.getLogger(__name__)
@@ -534,7 +534,7 @@ async def async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.
534534
await self._initialize()
535535

536536
# Capture protocol version from request headers
537-
self.context.protocol_version = request.headers.get(MCP_PROTOCOL_VERSION)
537+
self.context.protocol_version = request.headers.get(MCP_PROTOCOL_VERSION_HEADER)
538538

539539
if not self.context.is_token_valid() and self.context.can_refresh_token():
540540
# Try to refresh token

‎src/mcp/client/auth/utils.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
from pydantic import AnyUrl, ValidationError
66

77
from mcp.client.auth import OAuthFlowError, OAuthRegistrationError, OAuthTokenError
8-
from mcp.client.streamable_http import MCP_PROTOCOL_VERSION
98
from mcp.shared.auth import (
109
OAuthClientInformationFull,
1110
OAuthClientMetadata,
1211
OAuthMetadata,
1312
OAuthToken,
1413
ProtectedResourceMetadata,
1514
)
15+
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
1616
from mcp.types import LATEST_PROTOCOL_VERSION
1717

1818

@@ -273,7 +273,7 @@ def validate_metadata_issuer(oauth_metadata: OAuthMetadata, expected_issuer: str
273273

274274

275275
def create_oauth_metadata_request(url: str) -> Request:
276-
return Request("GET", url, headers={MCP_PROTOCOL_VERSION: LATEST_PROTOCOL_VERSION})
276+
return Request("GET", url, headers={MCP_PROTOCOL_VERSION_HEADER: LATEST_PROTOCOL_VERSION})
277277

278278

279279
def create_client_registration_request(

‎src/mcp/client/session.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from mcp.shared.message import ClientMessageMetadata, SessionMessage
2222
from mcp.shared.session import RequestResponder
2323
from mcp.shared.transport_context import TransportContext
24-
from mcp.shared.version import MODERN_PROTOCOL_VERSIONS, SUPPORTED_PROTOCOL_VERSIONS
24+
from mcp.shared.version import HANDSHAKE_PROTOCOL_VERSIONS, MODERN_PROTOCOL_VERSIONS
2525
from mcp.types import (
2626
CLIENT_CAPABILITIES_META_KEY,
2727
CLIENT_INFO_META_KEY,
@@ -320,15 +320,15 @@ async def initialize(self) -> types.InitializeResult:
320320
params=types.InitializeRequestParams(
321321
protocol_version=self._pinned_version
322322
if self._pinned_version is not None
323-
else types.LATEST_PROTOCOL_VERSION,
323+
else HANDSHAKE_PROTOCOL_VERSIONS[-1],
324324
capabilities=capabilities,
325325
client_info=self._client_info,
326326
),
327327
),
328328
types.InitializeResult,
329329
)
330330

331-
if result.protocol_version not in SUPPORTED_PROTOCOL_VERSIONS:
331+
if result.protocol_version not in HANDSHAKE_PROTOCOL_VERSIONS:
332332
raise RuntimeError(f"Unsupported protocol version from the server: {result.protocol_version}")
333333

334334
self._initialize_result = result

‎src/mcp/client/streamable_http.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from mcp.shared._compat import resync_tracer
2121
from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams
2222
from mcp.shared._httpx_utils import create_mcp_http_client
23+
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
2324
from mcp.shared.message import ClientMessageMetadata, SessionMessage
2425
from mcp.shared.version import MODERN_PROTOCOL_VERSIONS
2526
from mcp.types import (
@@ -46,7 +47,6 @@
4647
StreamReader = ContextReceiveStream[SessionMessage]
4748

4849
MCP_SESSION_ID = "mcp-session-id"
49-
MCP_PROTOCOL_VERSION = "mcp-protocol-version"
5050
MCP_METHOD = "mcp-method"
5151
MCP_NAME = "mcp-name"
5252
LAST_EVENT_ID = "last-event-id"
@@ -138,7 +138,7 @@ def _prepare_headers(self) -> dict[str, str]:
138138
if self.session_id:
139139
headers[MCP_SESSION_ID] = self.session_id
140140
if self.protocol_version:
141-
headers[MCP_PROTOCOL_VERSION] = self.protocol_version
141+
headers[MCP_PROTOCOL_VERSION_HEADER] = self.protocol_version
142142
return headers
143143

144144
def _is_initialization_request(self, message: JSONRPCMessage) -> bool:

‎src/mcp/server/auth/routes.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
from mcp.server.auth.middleware.client_auth import ClientAuthenticator
1818
from mcp.server.auth.provider import OAuthAuthorizationServerProvider
1919
from mcp.server.auth.settings import ClientRegistrationOptions, RevocationOptions
20-
from mcp.server.streamable_http import MCP_PROTOCOL_VERSION_HEADER
2120
from mcp.shared.auth import OAuthMetadata, ProtectedResourceMetadata
21+
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
2222

2323

2424
def validate_issuer_url(url: AnyHttpUrl):

‎src/mcp/server/connection.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
from mcp.shared.dispatcher import CallOptions, Outbound
3232
from mcp.shared.exceptions import MCPDeprecationWarning, NoBackChannelError
3333
from mcp.shared.peer import Meta, dump_params
34+
from mcp.shared.version import HANDSHAKE_PROTOCOL_VERSIONS
3435
from mcp.types import (
35-
LATEST_PROTOCOL_VERSION,
3636
ClientCapabilities,
3737
CreateMessageRequest,
3838
CreateMessageResult,
@@ -192,12 +192,14 @@ def for_loop(
192192
193193
Not born-ready: `initialized` is set later by the kernel when
194194
`notifications/initialized` arrives. `protocol_version` is seeded from
195-
the transport hint (or `LATEST_PROTOCOL_VERSION`) so it's never `None`;
195+
the transport hint (or `HANDSHAKE_PROTOCOL_VERSIONS[-1]`) so it's never `None`;
196196
the handshake overwrites it once negotiated.
197197
"""
198198
return cls(
199199
outbound,
200-
protocol_version=protocol_version_hint if protocol_version_hint is not None else LATEST_PROTOCOL_VERSION,
200+
protocol_version=protocol_version_hint
201+
if protocol_version_hint is not None
202+
else HANDSHAKE_PROTOCOL_VERSIONS[-1],
201203
session_id=session_id,
202204
)
203205

‎src/mcp/server/runner.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@
3636
from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher, handler_exception_to_error_data
3737
from mcp.shared.message import ServerMessageMetadata, SessionMessage
3838
from mcp.shared.transport_context import TransportContext
39-
from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
39+
from mcp.shared.version import HANDSHAKE_PROTOCOL_VERSIONS
4040
from mcp.types import (
4141
INTERNAL_ERROR,
4242
INVALID_PARAMS,
43-
LATEST_PROTOCOL_VERSION,
4443
METHOD_NOT_FOUND,
4544
ErrorData,
4645
Implementation,
@@ -414,7 +413,7 @@ def _negotiate_initialize(params: Mapping[str, Any] | None) -> tuple[InitializeR
414413
"""Validate `initialize` params and pick the protocol version."""
415414
init = InitializeRequestParams.model_validate(params or {}, by_name=False)
416415
requested = init.protocol_version
417-
negotiated = requested if requested in SUPPORTED_PROTOCOL_VERSIONS else LATEST_PROTOCOL_VERSION
416+
negotiated = requested if requested in HANDSHAKE_PROTOCOL_VERSIONS else HANDSHAKE_PROTOCOL_VERSIONS[-1]
418417
return init, negotiated
419418

420419
def _handle_initialize(self, params: Mapping[str, Any] | None) -> InitializeResult:

‎src/mcp/server/streamable_http.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from mcp.server.transport_security import TransportSecurityMiddleware, TransportSecuritySettings
2929
from mcp.shared._context_streams import ContextReceiveStream, ContextSendStream, create_context_streams
3030
from mcp.shared._stream_protocols import ReadStream, WriteStream
31+
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
3132
from mcp.shared.message import ServerMessageMetadata, SessionMessage
3233
from mcp.shared.version import is_version_at_least
3334
from mcp.types import (
@@ -50,7 +51,6 @@
5051

5152
# Header names
5253
MCP_SESSION_ID_HEADER = "mcp-session-id"
53-
MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version"
5454
LAST_EVENT_ID_HEADER = "last-event-id"
5555

5656
# Content types
@@ -818,7 +818,7 @@ async def _handle_unsupported_request(self, request: Request, send: Send) -> Non
818818

819819
async def _validate_request_headers(self, request: Request, send: Send) -> bool:
820820
# Protocol-version validation lives in the manager's era-routing: only
821-
# values in `SUPPORTED_PROTOCOL_VERSIONS` (or no header at all) reach
821+
# values in `HANDSHAKE_PROTOCOL_VERSIONS` (or no header at all) reach
822822
# this transport, so the legacy version-gate is gone.
823823
return await self._validate_session(request, send)
824824

‎src/mcp/server/streamable_http_manager.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@
1919
from mcp.server.connection import Connection
2020
from mcp.server.runner import serve_connection, serve_loop
2121
from mcp.server.streamable_http import (
22-
MCP_PROTOCOL_VERSION_HEADER,
2322
MCP_SESSION_ID_HEADER,
2423
EventStore,
2524
StreamableHTTPServerTransport,
2625
)
2726
from mcp.server.transport_security import TransportSecuritySettings
2827
from mcp.shared._compat import resync_tracer
28+
from mcp.shared.inbound import MCP_PROTOCOL_VERSION_HEADER
2929
from mcp.shared.jsonrpc_dispatcher import JSONRPCDispatcher
3030
from mcp.shared.transport_context import TransportContext
31-
from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS
31+
from mcp.shared.version import HANDSHAKE_PROTOCOL_VERSIONS
3232
from mcp.types import DEFAULT_NEGOTIATED_VERSION, INVALID_REQUEST, ErrorData, JSONRPCError
3333

3434
if TYPE_CHECKING:
@@ -169,7 +169,7 @@ async def handle_request(self, scope: Scope, receive: Receive, send: Send) -> No
169169
# and return a structured rejection. 2025 paths below remain unchanged.
170170
header = MCP_PROTOCOL_VERSION_HEADER.encode("ascii")
171171
pv = next((v.decode("latin-1") for k, v in scope["headers"] if k == header), None)
172-
if pv is not None and pv not in SUPPORTED_PROTOCOL_VERSIONS:
172+
if pv is not None and pv not in HANDSHAKE_PROTOCOL_VERSIONS:
173173
await handle_modern_request(self.app, self.security_settings, self._lifespan_state, scope, receive, send)
174174
return
175175

0 commit comments

Comments
 (0)