-
Notifications
You must be signed in to change notification settings - Fork 574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add lifespan context manager support #203
Conversation
Adds a context manager based lifespan API in mcp.server.lowlevel.server to manage server lifecycles in a type-safe way. This enables servers to: - Initialize resources on startup and clean them up on shutdown - Pass context data from startup to request handlers - Support async startup/shutdown operations
Adds support for the lifespan API to FastMCP server, enabling: - Simple setup with FastMCP constructor - Type-safe context passing to tools and handlers - Configuration via Settings class
Adds comprehensive tests for lifespan functionality: - Tests for both low-level Server and FastMCP classes - Coverage for startup, shutdown, and context access - Verifies context passing to request handlers
Add comprehensive documentation for lifespan support: - Add usage examples for both Server and FastMPC classes - Document startup/shutdown patterns - Show context access in tools and handlers - Clean up spacing in test files
Replace nested context managers with AsyncExitStack to ensure proper cleanup order during server shutdown and make the code more maintainable.
91d9271
to
fddba00
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly questions/confusions from me, overall looks good!
async with AsyncExitStack() as stack: | ||
lifespan_context = await stack.enter_async_context(self.lifespan(self)) | ||
session = await stack.enter_async_context( | ||
ServerSession(read_stream, write_stream, initialization_options) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this different from doing async with lifespan_context, session
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The AsyncExitStack() version provides more granular control over multiple context managers. If an error occurs during the second enter_async_context, the first one will still be properly cleaned up.
Chatted more with Claude about it, we came to the. conclusion that AsyncExitStack is better here.
src/mcp/server/lowlevel/server.py
Outdated
) | ||
|
||
async def _handle_request( | ||
self, | ||
message: RequestResponder, | ||
req: Any, | ||
session: ServerSession, | ||
lifespan_context: object, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lifespan_context: object, | |
lifespan_context: LifespanResultT, |
- Add proper generic parameter for lifespan context type - Update README with TypedDict example for strong typing - Fix context variable initialization in server - Improve property return type safety - Remove redundant documentation - Ensure compatibility with existing tests
Summary
Test plan