Skip to content

Repository files navigation

PyPI version Supported Python versions Downloads Coverage CI License GitHub stars uv Ruff ty

modern-di integration for FastMCP.

Full guide: FastMCP integration docs

Usage example: examples/

Installation

uv add modern-di-fastmcp      # or: pip install modern-di-fastmcp

Usage

setup_di attaches the root container to the server and adds a middleware that builds a request container for every MCP request. FromDI, used as a parameter's default value, resolves a provider (or type) from it and keeps the parameter out of the tool schema.

import dataclasses

import fastmcp
from modern_di import Container, Group, Scope, providers
from modern_di_fastmcp import FromDI, setup_di


@dataclasses.dataclass(kw_only=True)
class Settings:
    greeting: str = "Hello"


@dataclasses.dataclass(kw_only=True)
class GreetingService:
    settings: Settings  # auto-injected by type


class Dependencies(Group):
    settings = providers.Factory(scope=Scope.APP, creator=Settings)
    service = providers.Factory(scope=Scope.REQUEST, creator=GreetingService)


mcp = fastmcp.FastMCP("greeter")
container = Container(groups=[Dependencies])
setup_di(mcp, container)
container.validate()  # optional fail-fast; must come after setup_di registers its providers


@mcp.tool
def greet(name: str, service: GreetingService = FromDI(Dependencies.service)) -> str:  # noqa: B008
    return f"{service.settings.greeting}, {name}!"

FromDI must be the default value, not Annotated metadata: FastMCP keeps an Annotated parameter in the schema and asks the client for it. When the parameter's type has no schema, as with a plain class, FastMCP itself refuses the tool when it is defined. When it has one, as with a dataclass, the server raises TypeError at startup naming the parameter. The check covers the server's own tools, resources and prompts, not those of a mounted server or ones added after startup.

To stop ruff's B008 from flagging every FromDI default, add it to your ruff config:

[tool.ruff.lint.flake8-bugbear]
extend-immutable-calls = ["modern_di_fastmcp.FromDI"]

The current fastmcp.Context is resolvable within DI via the pre-built fastmcp_context_provider context provider.

API

Symbol Description
setup_di(server, container, *, manage_lifespan=True) Attaches the container to the server and adds the DI middleware. The server's lifespan opens the container at startup and closes it with close_async() at shutdown. When several apps share one container, exactly one of them should own its lifespan: pass manage_lifespan=False to every other setup_di, or the first app to stop closes the container for the rest. At startup it raises TypeError for a FromDI used inside Annotated. Raises RuntimeError when called a second time for the same server. Returns the container
FromDI(dependency) Parameter default that resolves a provider (or type) from the request container. Raises RuntimeError naming setup_di when no request container is active, including in a background task (task=True), which FastMCP runs outside middleware
fetch_di_container(server) Returns the root container attached to the server. Raises RuntimeError when setup_di was not called
fastmcp_context_provider ContextProvider for the current fastmcp.Context (REQUEST scope)

📦 PyPI

📝 License

Part of modern-python

Built on modern-di, a dependency-injection framework with an IoC container and scopes.

Browse the full list of templates and libraries in modern-python; the org profile has the categorized index.

Releases

Packages

Contributors

Languages