Lifespans let you run code once when the server starts and clean up when it stops. Unlike per-session handlers, lifespans run exactly once regardless of how many clients connect.
Basic Usage
Use the @lifespan decorator to define a lifespan:
from fastmcp import FastMCP
from fastmcp.server.lifespan import lifespan
@lifespan
async def app_lifespan(server):
# Setup: runs once when server starts
print("Starting up...")
try:
yield {"started_at": "2024-01-01"}
finally:
# Teardown: runs when server stops
print("Shutting down...")
mcp = FastMCP("MyServer", lifespan=app_lifespan)
The dict you yield becomes the lifespan context, accessible from tools.
Always use try/finally for cleanup code to ensure it runs even if the server is cancelled.
Accessing Lifespan Context
Access the lifespan context in tools via ctx.lifespan_context:
from fastmcp import FastMCP, Context
from fastmcp.server.lifespan import lifespan
@lifespan
async def app_lifespan(server):
# Initialize shared state
data = {"users": ["alice", "bob"]}
yield {"data": data}
mcp = FastMCP("MyServer", lifespan=app_lifespan)
@mcp.tool
def list_users(ctx: Context) -> list[str]:
data = ctx.lifespan_context["data"]
return data["users"]
Composing Lifespans
Compose multiple lifespans with the | operator:
from fastmcp import FastMCP
from fastmcp.server.lifespan import lifespan
@lifespan
async def config_lifespan(server):
config = {"debug": True, "version": "1.0"}
yield {"config": config}
@lifespan
async def data_lifespan(server):
data = {"items": []}
yield {"data": data}
# Compose with |
mcp = FastMCP("MyServer", lifespan=config_lifespan | data_lifespan)
Composed lifespans:
- Enter in order (left to right)
- Exit in reverse order (right to left)
- Merge their context dicts (later values overwrite earlier on conflict)
Backwards Compatibility
Existing @asynccontextmanager lifespans still work when passed directly to FastMCP:
from contextlib import asynccontextmanager
from fastmcp import FastMCP
@asynccontextmanager
async def legacy_lifespan(server):
yield {"key": "value"}
mcp = FastMCP("MyServer", lifespan=legacy_lifespan)
To compose an @asynccontextmanager function with @lifespan functions, wrap it with ContextManagerLifespan:
from contextlib import asynccontextmanager
from fastmcp.server.lifespan import lifespan, ContextManagerLifespan
@asynccontextmanager
async def legacy_lifespan(server):
yield {"legacy": True}
@lifespan
async def new_lifespan(server):
yield {"new": True}
# Wrap the legacy lifespan explicitly for composition
combined = ContextManagerLifespan(legacy_lifespan) | new_lifespan
With FastAPI
When mounting FastMCP into FastAPI, use combine_lifespans to run both your app’s lifespan and the MCP server’s lifespan:
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastmcp import FastMCP
from fastmcp.utilities.lifespan import combine_lifespans
@asynccontextmanager
async def app_lifespan(app):
print("FastAPI starting...")
yield
print("FastAPI shutting down...")
mcp = FastMCP("Tools")
mcp_app = mcp.http_app()
app = FastAPI(lifespan=combine_lifespans(app_lifespan, mcp_app.lifespan))
app.mount("/mcp", mcp_app)
See the FastAPI integration guide for full details.