Skip to content

feat(http): trigger lifespan events when using mount_http() (adopted from #311, closes #256) - #339

Open
K4bain wants to merge 1 commit into
tadata-org:mainfrom
K4bain:fix/http-lifespan
Open

feat(http): trigger lifespan events when using mount_http() (adopted from #311, closes #256)#339
K4bain wants to merge 1 commit into
tadata-org:mainfrom
K4bain:fix/http-lifespan

Conversation

@K4bain

@K4bain K4bain commented Aug 27, 2026

Copy link
Copy Markdown

Adopted from #311 (original patch by @Emeralden) — rebased onto current main and verified. Closes #256.

Why this is needed

mount_http() starts the StreamableHTTP session manager lazily on the first request. Consequences:

  • The MCP session manager lifecycle runs completely outside the app's lifespan — lifespan-initialised resources (DB pools, caches) are not guaranteed ready, and shutdown never drains in-flight sessions.
  • You cannot use add_event_handler() to fix this: when the user passed lifespan= to FastAPI(), Starlette stores it as lifespan_context and bypasses on_startup/on_shutdown entirely.

The fix wraps the host app's lifespan_context with an asynccontextmanager that runs the user's lifespan first, then starts the session manager, and on teardown stops the session manager before user shutdown. Wrapping lifespan_context works for both plain apps (_DefaultLifespan still invokes on_startup/on_shutdown) and custom lifespans.

Also adds public startup()/shutdown() methods to FastApiHttpSessionManager and keeps the lazy start as a fallback for apps that never run a lifespan.

Verification

  • New tests/test_lifespan.py (6 tests): lifespan events fire with mount_http(), initialize works over ASGI, apps without lifespan still work, and ordering asserts user_startup < mcp_startup and mcp_shutdown < user_shutdown. The ordering test fails on main (session manager never starts during lifespan); all pass with the fix.
  • Full unit suite: 75 passed (69 existing + 6 new).

Credit to @Emeralden for the original report and patch in #311.

mount_http() left the StreamableHTTP session manager to start lazily
on the first request, so user lifespan events ran detached from the
MCP session manager and tools could not rely on lifespan-initialised
resources, and shutdown never drained in-flight sessions. Wrap the
host app's lifespan_context (works for both custom lifespans and
Starlette's default) so the session manager starts after user startup
and stops before user shutdown.

Adopted from tadata-org#311 (original patch by @Emeralden), closes tadata-org#256.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] lifespan context manager not triggered when using mount_http() in fastapi-mcp 0.4.0

1 participant