@@ -1597,3 +1597,75 @@ async def bad_client():
15971597 assert isinstance (result , InitializeResult )
15981598 tools = await session .list_tools ()
15991599 assert tools .tools
1600+
1601+
1602+ @pytest .mark .anyio
1603+ async def test_client_unexpected_content_type_raises_mcp_error ():
1604+ """Test that unexpected content types raise McpError instead of just printing."""
1605+ # Use a real server that returns HTML to test the actual behavior
1606+ from starlette .responses import HTMLResponse
1607+ from starlette .routing import Route
1608+
1609+ # Create a simple server that returns HTML instead of MCP JSON
1610+ async def html_endpoint (request : Request ):
1611+ return HTMLResponse ("<html><body>Not an MCP server</body></html>" )
1612+
1613+ app = Starlette (routes = [
1614+ Route ("/mcp" , html_endpoint , methods = ["GET" , "POST" ]),
1615+ ])
1616+
1617+ # Start server on a random port using a simpler approach
1618+ with socket .socket () as s :
1619+ s .bind (("127.0.0.1" , 0 ))
1620+ port = s .getsockname ()[1 ]
1621+
1622+ # Use a thread instead of multiprocessing to avoid pickle issues
1623+ import threading
1624+ import asyncio
1625+
1626+ def run_server ():
1627+ loop = asyncio .new_event_loop ()
1628+ asyncio .set_event_loop (loop )
1629+ uvicorn .run (app , host = "127.0.0.1" , port = port , log_level = "error" )
1630+
1631+ server_thread = threading .Thread (target = run_server , daemon = True )
1632+ server_thread .start ()
1633+
1634+ try :
1635+ # Give server time to start
1636+ await asyncio .sleep (0.5 )
1637+
1638+ server_url = f"http://127.0.0.1:{ port } "
1639+
1640+ # Test that the client raises McpError when server returns HTML
1641+ with pytest .raises (ExceptionGroup ) as exc_info :
1642+ async with streamablehttp_client (f"{ server_url } /mcp" ) as (
1643+ read_stream ,
1644+ write_stream ,
1645+ _ ,
1646+ ):
1647+ async with ClientSession (read_stream , write_stream ) as session :
1648+ await session .initialize ()
1649+
1650+ # Extract the McpError from the ExceptionGroup (handle nested groups)
1651+ mcp_error = None
1652+
1653+ def find_mcp_error (exc_group ):
1654+ for exc in exc_group .exceptions :
1655+ if isinstance (exc , McpError ):
1656+ return exc
1657+ elif isinstance (exc , ExceptionGroup ):
1658+ result = find_mcp_error (exc )
1659+ if result :
1660+ return result
1661+ return None
1662+
1663+ mcp_error = find_mcp_error (exc_info .value )
1664+
1665+ assert mcp_error is not None , f"Expected McpError in ExceptionGroup hierarchy"
1666+ assert "Unexpected content type" in str (mcp_error )
1667+ assert "text/html" in str (mcp_error )
1668+
1669+ finally :
1670+ # Server thread will be cleaned up automatically as daemon
1671+ pass
0 commit comments