fix: add lifespan context manager to StreamableHTTP mounting examples
Summary
This PR fixes issue #1484 by adding the missing lifespan context manager to the StreamableHTTP mounting examples.
Problem
The current examples in examples/snippets/servers/ for mounting StreamableHTTP servers to existing ASGI applications are incomplete. When developers follow these examples, they encounter:
RuntimeError: Task group is not initialized. Make sure to use run().
This happens because the session manager is never properly initialized.
Solution
Added the proper lifespan context manager pattern to initialize the session manager before handling requests:
@contextlib.asynccontextmanager
async def lifespan(app: Starlette):
async with mcp.session_manager.run():
yield
This pattern was already correctly implemented in streamable_starlette_mount.py but was missing from:
-
streamable_http_basic_mounting.py -
streamable_http_host_mounting.py -
streamable_http_multiple_servers.py
Files Changed
-
examples/snippets/servers/streamable_http_basic_mounting.py- Added lifespan for single server -
examples/snippets/servers/streamable_http_host_mounting.py- Added lifespan for host-based routing -
examples/snippets/servers/streamable_http_multiple_servers.py- Added combined lifespan for multiple servers using AsyncExitStack
Test Plan
- [x] Code passes
ruff format - [x] Code passes
ruff check - [x] Code passes
pyrighttype checking - [x] Manual testing: Run each example with uvicorn and verify no RuntimeError
Closes #1484
Manual Verification
I manually tested the fix by running the examples with uvicorn:
With the Fix (Fixed Example)
uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --port 8765
Server logs:
INFO: Started server process
INFO: Waiting for application startup.
INFO: StreamableHTTP session manager started <-- Key: session manager initializes!
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8765
Test request:
curl -X POST http://localhost:8765/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0"}}, "id": 1}'
Response: ✅ Success
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{...},"serverInfo":{"name":"My App","version":"1.22.1.dev10+02b7889"}}}
Without the Fix (Reproducing the Bug)
Created a test file without the lifespan context manager:
# Missing lifespan - reproduces the bug
app = Starlette(
routes=[Mount("/", app=mcp.streamable_http_app())]
# No lifespan parameter!
)
Server logs:
INFO: Started server process
INFO: Waiting for application startup.
INFO: Application startup complete. <-- Note: NO "session manager started" message
INFO: Uvicorn running on http://0.0.0.0:8766
Same test request returns: ❌ Failure
Internal Server Error
Error in logs:
RuntimeError: Task group is not initialized. Make sure to use run().
Conclusion
The fix is verified working. Adding the lifespan context manager properly initializes the session manager, which resolves the RuntimeError reported in #1484.