Getting started¶
Install¶
The package requires Python 3.11+, Dishka 1.10.1+, FastMCP 4.0.0 through the
latest 4.x release, and the MCP SDK (mcp) 2.x, which FastMCP installs.
Configure the server¶
Create the container before the FastMCP server so the lifespan can close it on shutdown:
from dishka import Provider, Scope, make_async_container, provide
from fastmcp import FastMCP
from dishka_fastmcp import FromDishka, dishka_lifespan, inject, setup_dishka
class Catalog:
_prices: dict[str, int] = {'book': 12, 'pen': 2}
def price(self, item: str) -> int:
return self._prices.get(item, 0)
class AppProvider(Provider):
catalog = provide(Catalog, scope=Scope.REQUEST)
container = make_async_container(AppProvider())
mcp = FastMCP('example', lifespan=dishka_lifespan(container))
setup_dishka(container, mcp)
@mcp.tool
@inject
async def get_price(item: str, catalog: FromDishka[Catalog]) -> int:
return catalog.price(item)
Decorator order matters
Put @inject below @mcp.tool, @mcp.resource, or @mcp.prompt. FastMCP
must inspect the signature after Dishka parameters have been removed.
@inject above a FastMCP decorator fails at import: FastMCP has registered
the bare function by then, and the client would fill in the dependency
itself.
Resources and prompts¶
The same decorator works for every supported FastMCP component:
@mcp.resource('users://{user_id}')
@inject
async def user(user_id: str, repo: FromDishka[UserRepo]) -> dict:
return await repo.get(user_id)
@mcp.prompt
@inject
async def summarize(text: str, service: FromDishka[Summarizer]) -> str:
return await service.run(text)
Sync handlers¶
Use a sync Dishka container for regular sync handlers. FastMCP runs them in a worker thread, and dishka-fastmcp keeps dependency creation, usage, and cleanup in that thread.