@@ -1425,3 +1425,118 @@ async def test_streamablehttp_request_context_isolation(
14251425 assert ctx ["headers" ].get ("x-request-id" ) == f"request-{ i } "
14261426 assert ctx ["headers" ].get ("x-custom-value" ) == f"value-{ i } "
14271427 assert ctx ["headers" ].get ("authorization" ) == f"Bearer token-{ i } "
1428+
1429+
1430+ @pytest .mark .anyio
1431+ async def test_client_includes_protocol_version_header_after_init (
1432+ context_aware_server , basic_server_url
1433+ ):
1434+ """Test that client includes MCP-Protocol-Version header after initialization."""
1435+ async with streamablehttp_client (f"{ basic_server_url } /mcp" ) as (
1436+ read_stream ,
1437+ write_stream ,
1438+ _ ,
1439+ ):
1440+ async with ClientSession (read_stream , write_stream ) as session :
1441+ # Initialize and get the negotiated version
1442+ init_result = await session .initialize ()
1443+ negotiated_version = init_result .protocolVersion
1444+
1445+ # Call a tool that echoes headers to verify the header is present
1446+ tool_result = await session .call_tool ("echo_headers" , {})
1447+
1448+ assert len (tool_result .content ) == 1
1449+ assert isinstance (tool_result .content [0 ], TextContent )
1450+ headers_data = json .loads (tool_result .content [0 ].text )
1451+
1452+ # Verify protocol version header is present
1453+ assert "mcp-protocol-version" in headers_data
1454+ assert headers_data ["mcp-protocol-version" ] == negotiated_version
1455+
1456+
1457+ def test_server_validates_protocol_version_header (basic_server , basic_server_url ):
1458+ """Test that server returns 400 Bad Request version header is missing or invalid."""
1459+ # First initialize a session to get a valid session ID
1460+ init_response = requests .post (
1461+ f"{ basic_server_url } /mcp" ,
1462+ headers = {
1463+ "Accept" : "application/json, text/event-stream" ,
1464+ "Content-Type" : "application/json" ,
1465+ },
1466+ json = INIT_REQUEST ,
1467+ )
1468+ assert init_response .status_code == 200
1469+ session_id = init_response .headers .get (MCP_SESSION_ID_HEADER )
1470+
1471+ # Test request without MCP-Protocol-Version header (should fail)
1472+ response = requests .post (
1473+ f"{ basic_server_url } /mcp" ,
1474+ headers = {
1475+ "Accept" : "application/json, text/event-stream" ,
1476+ "Content-Type" : "application/json" ,
1477+ MCP_SESSION_ID_HEADER : session_id ,
1478+ },
1479+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-1" },
1480+ )
1481+ assert response .status_code == 400
1482+ assert (
1483+ "MCP-Protocol-Version" in response .text
1484+ or "protocol version" in response .text .lower ()
1485+ )
1486+
1487+ # Test request with invalid protocol version (should fail)
1488+ response = requests .post (
1489+ f"{ basic_server_url } /mcp" ,
1490+ headers = {
1491+ "Accept" : "application/json, text/event-stream" ,
1492+ "Content-Type" : "application/json" ,
1493+ MCP_SESSION_ID_HEADER : session_id ,
1494+ "MCP-Protocol-Version" : "invalid-version" ,
1495+ },
1496+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-2" },
1497+ )
1498+ assert response .status_code == 400
1499+ assert (
1500+ "MCP-Protocol-Version" in response .text
1501+ or "protocol version" in response .text .lower ()
1502+ )
1503+
1504+ # Test request with unsupported protocol version (should fail)
1505+ response = requests .post (
1506+ f"{ basic_server_url } /mcp" ,
1507+ headers = {
1508+ "Accept" : "application/json, text/event-stream" ,
1509+ "Content-Type" : "application/json" ,
1510+ MCP_SESSION_ID_HEADER : session_id ,
1511+ "MCP-Protocol-Version" : "1999-01-01" , # Very old unsupported version
1512+ },
1513+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-3" },
1514+ )
1515+ assert response .status_code == 400
1516+ assert (
1517+ "MCP-Protocol-Version" in response .text
1518+ or "protocol version" in response .text .lower ()
1519+ )
1520+
1521+ # Test request with valid protocol version (should succeed)
1522+ init_data = None
1523+ assert init_response .headers .get ("Content-Type" ) == "text/event-stream"
1524+ for line in init_response .text .splitlines ():
1525+ if line .startswith ("data: " ):
1526+ init_data = json .loads (line [6 :])
1527+ break
1528+
1529+ assert init_data is not None
1530+ negotiated_version = init_data ["result" ]["protocolVersion" ]
1531+
1532+ response = requests .post (
1533+ f"{ basic_server_url } /mcp" ,
1534+ headers = {
1535+ "Accept" : "application/json, text/event-stream" ,
1536+ "Content-Type" : "application/json" ,
1537+ MCP_SESSION_ID_HEADER : session_id ,
1538+ "MCP-Protocol-Version" : negotiated_version ,
1539+ },
1540+ json = {"jsonrpc" : "2.0" , "method" : "tools/list" , "id" : "test-4" },
1541+ )
1542+ assert response .status_code == 200
0 commit comments