@@ -1358,3 +1358,118 @@ async def test_streamablehttp_request_context_isolation(context_aware_server: No
13581358 assert ctx ["headers" ].get ("x-request-id" ) == f"request-{ i } "
13591359 assert ctx ["headers" ].get ("x-custom-value" ) == f"value-{ i } "
13601360 assert ctx ["headers" ].get ("authorization" ) == f"Bearer token-{ i } "
1361+
1362+
1363+ @pytest .mark .anyio
1364+ async def test_client_includes_protocol_version_header_after_init (
1365+ context_aware_server , basic_server_url
1366+ ):
1367+ """Test that client includes MCP-Protocol-Version header after initialization."""
1368+ async with streamablehttp_client (f"{ basic_server_url } /mcp" ) as (
1369+ read_stream ,
1370+ write_stream ,
1371+ _ ,
1372+ ):
1373+ async with ClientSession (read_stream , write_stream ) as session :
1374+ # Initialize and get the negotiated version
1375+ init_result = await session .initialize ()
1376+ negotiated_version = init_result .protocolVersion
1377+
1378+ # Call a tool that echoes headers to verify the header is present
1379+ tool_result = await session .call_tool ("echo_headers" , {})
1380+
1381+ assert len (tool_result .content ) == 1
1382+ assert isinstance (tool_result .content [0 ], TextContent )
1383+ headers_data = json .loads (tool_result .content [0 ].text )
1384+
1385+ # Verify protocol version header is present
1386+ assert "mcp-protocol-version" in headers_data
1387+ assert headers_data ["mcp-protocol-version" ] == negotiated_version
1388+
1389+
1390+ def test_server_validates_protocol_version_header (basic_server , basic_server_url ):
1391+ """Test that server returns 400 Bad Request version header is missing or invalid."""
1392+ # First initialize a session to get a valid session ID
1393+ init_response = requests .post (
1394+ f"{ basic_server_url } /mcp" ,
1395+ headers = {
1396+ "Accept" : "application/json, text/event-stream" ,
1397+ "Content-Type" : "application/json" ,
1398+ },
1399+ json = INIT_REQUEST ,
1400+ )
1401+ assert init_response .status_code == 200
1402+ session_id = init_response .headers .get (MCP_SESSION_ID_HEADER )
1403+
1404+ # Test request without MCP-Protocol-Version header (should fail)
1405+ response = requests .post (
1406+ f"{ basic_server_url } /mcp" ,
1407+ headers = {
1408+ "Accept" : "application/json, text/event-stream" ,
1409+ "Content-Type" : "application/json" ,
1410+ MCP_SESSION_ID_HEADER : session_id ,
1411+ },
1412+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-1" },
1413+ )
1414+ assert response .status_code == 400
1415+ assert (
1416+ "MCP-Protocol-Version" in response .text
1417+ or "protocol version" in response .text .lower ()
1418+ )
1419+
1420+ # Test request with invalid protocol version (should fail)
1421+ response = requests .post (
1422+ f"{ basic_server_url } /mcp" ,
1423+ headers = {
1424+ "Accept" : "application/json, text/event-stream" ,
1425+ "Content-Type" : "application/json" ,
1426+ MCP_SESSION_ID_HEADER : session_id ,
1427+ "MCP-Protocol-Version" : "invalid-version" ,
1428+ },
1429+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-2" },
1430+ )
1431+ assert response .status_code == 400
1432+ assert (
1433+ "MCP-Protocol-Version" in response .text
1434+ or "protocol version" in response .text .lower ()
1435+ )
1436+
1437+ # Test request with unsupported protocol version (should fail)
1438+ response = requests .post (
1439+ f"{ basic_server_url } /mcp" ,
1440+ headers = {
1441+ "Accept" : "application/json, text/event-stream" ,
1442+ "Content-Type" : "application/json" ,
1443+ MCP_SESSION_ID_HEADER : session_id ,
1444+ "MCP-Protocol-Version" : "1999-01-01" , # Very old unsupported version
1445+ },
1446+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-3" },
1447+ )
1448+ assert response .status_code == 400
1449+ assert (
1450+ "MCP-Protocol-Version" in response .text
1451+ or "protocol version" in response .text .lower ()
1452+ )
1453+
1454+ # Test request with valid protocol version (should succeed)
1455+ init_data = None
1456+ assert init_response .headers .get ("Content-Type" ) == "text/event-stream"
1457+ for line in init_response .text .splitlines ():
1458+ if line .startswith ("data: " ):
1459+ init_data = json .loads (line [6 :])
1460+ break
1461+
1462+ assert init_data is not None
1463+ negotiated_version = init_data ["result" ]["protocolVersion" ]
1464+
1465+ response = requests .post (
1466+ f"{ basic_server_url } /mcp" ,
1467+ headers = {
1468+ "Accept" : "application/json, text/event-stream" ,
1469+ "Content-Type" : "application/json" ,
1470+ MCP_SESSION_ID_HEADER : session_id ,
1471+ "MCP-Protocol-Version" : negotiated_version ,
1472+ },
1473+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-4" },
1474+ )
1475+ assert response .status_code == 200
0 commit comments