Repository navigation
feat: modern-di integration for FastMCP - #1
Merged
Merged
Conversation
… second setup_di, and tighten tests and docs after review
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements modern-python/modern-di#462, following the design settled in the issue comment.
Summary
setup_di(server, container, *, manage_lifespan=True):fastmcp_context_provider, and adds a lifespan provider and the DI middleware;close_async()at shutdown;manage_lifespan=False, another app sharing the container owns that.on_requesthook opens one request container per MCP request (tool call, resource read, prompt render, list call; not notifications) and holds it in aContextVar.FromDI(dependency)is a parameter default (a FastMCPDependency):RuntimeErrornamingsetup_diwhen no request container is active, which includes background tasks.fetch_di_container(server)returns the root container.Design decisions
FromDIis a parameter default, notAnnotatedmetadata. FastMCP keeps anAnnotatedparameter in the schema, and its resolver never reads that metadata. FastMCP gives no hook when@mcp.toolruns, so misuse is rejected in the lifespan instead:TypeErrorbefore the first request;docs/adr/0001-from-di-is-a-default-value.md.RuntimeErrorand aFastMCPError. FastMCP replaces any other error raised while resolving a dependency with "Failed to resolve dependency", which would hide thesetup_dihint. It passes aFastMCPErrorthrough unchanged, so the hint reaches the client.TYPE_CHECKING-only import is one example: FastMCP accepts such tools, and the check must not break them.setup_ditwice on one server raises. Otherwise the server would get a second middleware and a second lifespan.FromDIto bugbear'sextend-immutable-calls. The README shows users how to do the same, in place of# noqa: B008.Test plan
just lint-ci,just test-ci,just test-branch(20 tests, 100% line and branch coverage),just adr-checkCurrentContext()and FastMCPDepends()mixed withFromDI;mask_error_details=True.task=True) are not exercised, since that needsfastmcp[tasks]and Docket. The error message covers them.