From f528791baf4cea9ec8de59bcc4dd4b85533e445f Mon Sep 17 00:00:00 2001 From: ihrpr Date: Thu, 15 May 2025 18:27:28 +0100 Subject: [PATCH 1/2] accept older versions of client --- src/mcp/server/session.py | 6 ++- tests/server/test_session.py | 94 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/mcp/server/session.py b/src/mcp/server/session.py index 4f97c6cd6..ef5c5a3c3 100644 --- a/src/mcp/server/session.py +++ b/src/mcp/server/session.py @@ -52,6 +52,7 @@ async def handle_list_prompts(ctx: RequestContext) -> list[types.Prompt]: BaseSession, RequestResponder, ) +from mcp.shared.version import SUPPORTED_PROTOCOL_VERSIONS class InitializationState(Enum): @@ -150,13 +151,16 @@ async def _received_request( ): match responder.request.root: case types.InitializeRequest(params=params): + requested_version = params.protocolVersion self._initialization_state = InitializationState.Initializing self._client_params = params with responder: await responder.respond( types.ServerResult( types.InitializeResult( - protocolVersion=types.LATEST_PROTOCOL_VERSION, + protocolVersion=requested_version + if requested_version in SUPPORTED_PROTOCOL_VERSIONS + else types.LATEST_PROTOCOL_VERSION, capabilities=self._init_options.capabilities, serverInfo=types.Implementation( name=self._init_options.server_name, diff --git a/tests/server/test_session.py b/tests/server/test_session.py index f2f033588..846e1ab67 100644 --- a/tests/server/test_session.py +++ b/tests/server/test_session.py @@ -106,3 +106,97 @@ async def list_resources(): caps = server.get_capabilities(notification_options, experimental_capabilities) assert caps.prompts == PromptsCapability(listChanged=False) assert caps.resources == ResourcesCapability(subscribe=False, listChanged=False) + + +@pytest.mark.anyio +async def test_server_session_initialize_with_older_protocol_version(): + """Test that server accepts and responds with older protocol version (2024-11-05).""" + server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[ + SessionMessage + ](1) + client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[ + SessionMessage | Exception + ](1) + + received_initialized = False + received_protocol_version = None + + async def run_server(): + nonlocal received_initialized + + async with ServerSession( + client_to_server_receive, + server_to_client_send, + InitializationOptions( + server_name="mcp", + server_version="0.1.0", + capabilities=ServerCapabilities(), + ), + ) as server_session: + async for message in server_session.incoming_messages: + if isinstance(message, Exception): + raise message + + if isinstance(message, types.ClientNotification) and isinstance( + message.root, InitializedNotification + ): + received_initialized = True + return + + async def mock_client(): + nonlocal received_protocol_version + + # Send initialization request with older protocol version (2024-11-05) + await client_to_server_send.send( + SessionMessage( + types.JSONRPCMessage( + types.JSONRPCRequest( + jsonrpc="2.0", + id=1, + method="initialize", + params=types.InitializeRequestParams( + protocolVersion="2024-11-05", + capabilities=types.ClientCapabilities(), + clientInfo=types.Implementation( + name="test-client", version="1.0.0" + ), + ).model_dump(by_alias=True, mode="json", exclude_none=True), + ) + ) + ) + ) + + # Wait for the initialize response + init_response_message = await server_to_client_receive.receive() + assert isinstance(init_response_message.message.root, types.JSONRPCResponse) + result_data = init_response_message.message.root.result + init_result = types.InitializeResult.model_validate(result_data) + + # Check that the server responded with the requested protocol version + received_protocol_version = init_result.protocolVersion + assert received_protocol_version == "2024-11-05" + + # Send initialized notification + await client_to_server_send.send( + SessionMessage( + types.JSONRPCMessage( + types.JSONRPCNotification( + jsonrpc="2.0", + method="notifications/initialized", + ) + ) + ) + ) + + async with ( + client_to_server_send, + client_to_server_receive, + server_to_client_send, + server_to_client_receive, + anyio.create_task_group() as tg, + ): + tg.start_soon(run_server) + tg.start_soon(mock_client) + + assert received_initialized + assert received_protocol_version == "2024-11-05" From a5ee02d43a963dcca463681a0e7082b44adde176 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Thu, 15 May 2025 18:30:36 +0100 Subject: [PATCH 2/2] ruff --- tests/server/test_session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/server/test_session.py b/tests/server/test_session.py index 846e1ab67..1375df12f 100644 --- a/tests/server/test_session.py +++ b/tests/server/test_session.py @@ -110,7 +110,7 @@ async def list_resources(): @pytest.mark.anyio async def test_server_session_initialize_with_older_protocol_version(): - """Test that server accepts and responds with older protocol version (2024-11-05).""" + """Test that server accepts and responds with older protocol (2024-11-05).""" server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[ SessionMessage ](1)