Skip to content

Commit e0331e6

Browse files
committed
test: add test case for unexpected content type raising McpError
- Test verifies that when server returns non-MCP content type (HTML), client raises McpError instead of just printing - Handles nested ExceptionGroup structure from task groups - Confirms the fix works end-to-end
1 parent 029680c commit e0331e6

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

tests/shared/test_streamable_http.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)