|
5 | 5 | from unittest.mock import AsyncMock, patch |
6 | 6 |
|
7 | 7 | import anyio |
| 8 | +import httpx |
8 | 9 | import pytest |
9 | 10 | from starlette.types import Message |
10 | 11 |
|
| 12 | +from mcp import types |
| 13 | +from mcp.client.session import ClientSession |
| 14 | +from mcp.client.streamable_http import streamable_http_client |
11 | 15 | from mcp.server import streamable_http_manager |
12 | 16 | from mcp.server.lowlevel import Server |
13 | 17 | from mcp.server.streamable_http import MCP_SESSION_ID_HEADER, StreamableHTTPServerTransport |
@@ -313,3 +317,24 @@ async def mock_receive(): |
313 | 317 | assert error_data["id"] == "server-error" |
314 | 318 | assert error_data["error"]["code"] == INVALID_REQUEST |
315 | 319 | assert error_data["error"]["message"] == "Session not found" |
| 320 | + |
| 321 | + |
| 322 | +@pytest.mark.anyio |
| 323 | +async def test_e2e_streamable_http_server_cleanup(): |
| 324 | + host = "testserver" |
| 325 | + app = Server("test-server") |
| 326 | + |
| 327 | + @app.list_tools() |
| 328 | + async def list_tools(req: types.ListToolsRequest) -> types.ListToolsResult: |
| 329 | + return types.ListToolsResult(tools=[]) |
| 330 | + |
| 331 | + mcp_app = app.streamable_http_app(host=host) |
| 332 | + async with ( |
| 333 | + mcp_app.router.lifespan_context(mcp_app), |
| 334 | + httpx.ASGITransport(mcp_app) as transport, |
| 335 | + httpx.AsyncClient(transport=transport) as client, |
| 336 | + streamable_http_client(f"http://{host}/mcp", http_client=client) as (read_stream, write_stream), |
| 337 | + ClientSession(read_stream, write_stream) as session, |
| 338 | + ): |
| 339 | + await session.initialize() |
| 340 | + await session.list_tools() |
0 commit comments