2626from mcp .client .streamable_http import streamablehttp_client
2727from mcp .server import Server
2828from mcp .server .streamable_http import (
29+ MCP_PROTOCOL_VERSION_HEADER ,
2930 MCP_SESSION_ID_HEADER ,
3031 SESSION_ID_PATTERN ,
3132 EventCallback ,
@@ -560,11 +561,24 @@ def test_session_termination(basic_server, basic_server_url):
560561 )
561562 assert response .status_code == 200
562563
564+ # Extract negotiated protocol version from SSE response
565+ init_data = None
566+ assert response .headers .get ("Content-Type" ) == "text/event-stream"
567+ for line in response .text .splitlines ():
568+ if line .startswith ("data: " ):
569+ init_data = json .loads (line [6 :])
570+ break
571+ assert init_data is not None
572+ negotiated_version = init_data ["result" ]["protocolVersion" ]
573+
563574 # Now terminate the session
564575 session_id = response .headers .get (MCP_SESSION_ID_HEADER )
565576 response = requests .delete (
566577 f"{ basic_server_url } /mcp" ,
567- headers = {MCP_SESSION_ID_HEADER : session_id },
578+ headers = {
579+ MCP_SESSION_ID_HEADER : session_id ,
580+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
581+ },
568582 )
569583 assert response .status_code == 200
570584
@@ -595,16 +609,27 @@ def test_response(basic_server, basic_server_url):
595609 )
596610 assert response .status_code == 200
597611
598- # Now terminate the session
612+ # Extract negotiated protocol version from SSE response
613+ init_data = None
614+ assert response .headers .get ("Content-Type" ) == "text/event-stream"
615+ for line in response .text .splitlines ():
616+ if line .startswith ("data: " ):
617+ init_data = json .loads (line [6 :])
618+ break
619+ assert init_data is not None
620+ negotiated_version = init_data ["result" ]["protocolVersion" ]
621+
622+ # Now get the session ID
599623 session_id = response .headers .get (MCP_SESSION_ID_HEADER )
600624
601- # Try to use the terminated session
625+ # Try to use the session with proper headers
602626 tools_response = requests .post (
603627 mcp_url ,
604628 headers = {
605629 "Accept" : "application/json, text/event-stream" ,
606630 "Content-Type" : "application/json" ,
607631 MCP_SESSION_ID_HEADER : session_id , # Use the session ID we got earlier
632+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
608633 },
609634 json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "tools-1" },
610635 stream = True ,
@@ -646,12 +671,23 @@ def test_get_sse_stream(basic_server, basic_server_url):
646671 session_id = init_response .headers .get (MCP_SESSION_ID_HEADER )
647672 assert session_id is not None
648673
674+ # Extract negotiated protocol version from SSE response
675+ init_data = None
676+ assert init_response .headers .get ("Content-Type" ) == "text/event-stream"
677+ for line in init_response .text .splitlines ():
678+ if line .startswith ("data: " ):
679+ init_data = json .loads (line [6 :])
680+ break
681+ assert init_data is not None
682+ negotiated_version = init_data ["result" ]["protocolVersion" ]
683+
649684 # Now attempt to establish an SSE stream via GET
650685 get_response = requests .get (
651686 mcp_url ,
652687 headers = {
653688 "Accept" : "text/event-stream" ,
654689 MCP_SESSION_ID_HEADER : session_id ,
690+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
655691 },
656692 stream = True ,
657693 )
@@ -666,6 +702,7 @@ def test_get_sse_stream(basic_server, basic_server_url):
666702 headers = {
667703 "Accept" : "text/event-stream" ,
668704 MCP_SESSION_ID_HEADER : session_id ,
705+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
669706 },
670707 stream = True ,
671708 )
@@ -694,11 +731,22 @@ def test_get_validation(basic_server, basic_server_url):
694731 session_id = init_response .headers .get (MCP_SESSION_ID_HEADER )
695732 assert session_id is not None
696733
734+ # Extract negotiated protocol version from SSE response
735+ init_data = None
736+ assert init_response .headers .get ("Content-Type" ) == "text/event-stream"
737+ for line in init_response .text .splitlines ():
738+ if line .startswith ("data: " ):
739+ init_data = json .loads (line [6 :])
740+ break
741+ assert init_data is not None
742+ negotiated_version = init_data ["result" ]["protocolVersion" ]
743+
697744 # Test without Accept header
698745 response = requests .get (
699746 mcp_url ,
700747 headers = {
701748 MCP_SESSION_ID_HEADER : session_id ,
749+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
702750 },
703751 stream = True ,
704752 )
@@ -711,6 +759,7 @@ def test_get_validation(basic_server, basic_server_url):
711759 headers = {
712760 "Accept" : "application/json" ,
713761 MCP_SESSION_ID_HEADER : session_id ,
762+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
714763 },
715764 )
716765 assert response .status_code == 406
@@ -1004,6 +1053,7 @@ async def test_streamablehttp_client_resumption(event_server):
10041053 captured_resumption_token = None
10051054 captured_notifications = []
10061055 tool_started = False
1056+ captured_protocol_version = None
10071057
10081058 async def message_handler (
10091059 message : RequestResponder [types .ServerRequest , types .ClientResult ] | types .ServerNotification | Exception ,
@@ -1032,6 +1082,8 @@ async def on_resumption_token_update(token: str) -> None:
10321082 assert isinstance (result , InitializeResult )
10331083 captured_session_id = get_session_id ()
10341084 assert captured_session_id is not None
1085+ # Capture the negotiated protocol version
1086+ captured_protocol_version = result .protocolVersion
10351087
10361088 # Start a long-running tool in a task
10371089 async with anyio .create_task_group () as tg :
@@ -1064,10 +1116,12 @@ async def run_tool():
10641116 captured_notifications_pre = captured_notifications .copy ()
10651117 captured_notifications = []
10661118
1067- # Now resume the session with the same mcp-session-id
1119+ # Now resume the session with the same mcp-session-id and protocol version
10681120 headers = {}
10691121 if captured_session_id :
10701122 headers [MCP_SESSION_ID_HEADER ] = captured_session_id
1123+ if captured_protocol_version :
1124+ headers [MCP_PROTOCOL_VERSION_HEADER ] = captured_protocol_version
10711125
10721126 async with streamablehttp_client (f"{ server_url } /mcp" , headers = headers ) as (
10731127 read_stream ,
@@ -1413,7 +1467,7 @@ def test_server_validates_protocol_version_header(basic_server, basic_server_url
14131467 )
14141468 assert response .status_code == 400
14151469 assert (
1416- "MCP-Protocol-Version" in response .text
1470+ MCP_PROTOCOL_VERSION_HEADER in response .text
14171471 or "protocol version" in response .text .lower ()
14181472 )
14191473
@@ -1424,13 +1478,13 @@ def test_server_validates_protocol_version_header(basic_server, basic_server_url
14241478 "Accept" : "application/json, text/event-stream" ,
14251479 "Content-Type" : "application/json" ,
14261480 MCP_SESSION_ID_HEADER : session_id ,
1427- "MCP-Protocol-Version" : "invalid-version" ,
1481+ MCP_PROTOCOL_VERSION_HEADER : "invalid-version" ,
14281482 },
14291483 json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-2" },
14301484 )
14311485 assert response .status_code == 400
14321486 assert (
1433- "MCP-Protocol-Version" in response .text
1487+ MCP_PROTOCOL_VERSION_HEADER in response .text
14341488 or "protocol version" in response .text .lower ()
14351489 )
14361490
@@ -1441,13 +1495,13 @@ def test_server_validates_protocol_version_header(basic_server, basic_server_url
14411495 "Accept" : "application/json, text/event-stream" ,
14421496 "Content-Type" : "application/json" ,
14431497 MCP_SESSION_ID_HEADER : session_id ,
1444- "MCP-Protocol-Version" : "1999-01-01" , # Very old unsupported version
1498+ MCP_PROTOCOL_VERSION_HEADER : "1999-01-01" , # Very old unsupported version
14451499 },
14461500 json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-3" },
14471501 )
14481502 assert response .status_code == 400
14491503 assert (
1450- "MCP-Protocol-Version" in response .text
1504+ MCP_PROTOCOL_VERSION_HEADER in response .text
14511505 or "protocol version" in response .text .lower ()
14521506 )
14531507
@@ -1468,7 +1522,7 @@ def test_server_validates_protocol_version_header(basic_server, basic_server_url
14681522 "Accept" : "application/json, text/event-stream" ,
14691523 "Content-Type" : "application/json" ,
14701524 MCP_SESSION_ID_HEADER : session_id ,
1471- "MCP-Protocol-Version" : negotiated_version ,
1525+ MCP_PROTOCOL_VERSION_HEADER : negotiated_version ,
14721526 },
14731527 json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-4" },
14741528 )
0 commit comments