From 27d318810fc00c75445d204838ee37b8e3bfb3e0 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Tue, 20 Jan 2026 17:58:39 -0500 Subject: [PATCH] Fix FastAPI mounting examples in docs (#2962) --- docs/deployment/http.mdx | 23 +++++++++++++++-------- docs/integrations/fastapi.mdx | 10 +++++----- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/docs/deployment/http.mdx b/docs/deployment/http.mdx index 962c2e013..17ec9e825 100644 --- a/docs/deployment/http.mdx +++ b/docs/deployment/http.mdx @@ -344,13 +344,6 @@ Here's a quick example showing how to add MCP to an existing FastAPI application from fastapi import FastAPI from fastmcp import FastMCP -# Your existing API -api = FastAPI() - -@api.get("/api/status") -def status(): - return {"status": "ok"} - # Create your MCP server mcp = FastMCP("API Tools") @@ -359,14 +352,28 @@ def query_database(query: str) -> dict: """Run a database query""" return {"result": "data"} +# Create the MCP ASGI app with path="/" since we'll mount at /mcp +mcp_app = mcp.http_app(path="/") + +# Create FastAPI app with MCP lifespan (required for session management) +api = FastAPI(lifespan=mcp_app.lifespan) + +@api.get("/api/status") +def status(): + return {"status": "ok"} + # Mount MCP at /mcp -api.mount("/mcp", mcp.http_app()) +api.mount("/mcp", mcp_app) # Run with: uvicorn app:api --host 0.0.0.0 --port 8000 ``` Your existing API remains at `http://localhost:8000/api` while MCP is available at `http://localhost:8000/mcp`. + +Just like with Starlette, you **must** pass the lifespan from the MCP app to FastAPI. Without this, the session manager won't initialize properly and requests will fail. + + ## Mounting Authenticated Servers diff --git a/docs/integrations/fastapi.mdx b/docs/integrations/fastapi.mdx index 2ce28336d..abdc1d0e7 100644 --- a/docs/integrations/fastapi.mdx +++ b/docs/integrations/fastapi.mdx @@ -390,14 +390,14 @@ def get_user(user_id: int): When mounting MCP servers, always pass the lifespan context: ```python -# Correct - lifespan passed -mcp_app = mcp.http_app(path='/mcp') +# Correct - lifespan passed, path="/" since we mount at /mcp +mcp_app = mcp.http_app(path="/") app = FastAPI(lifespan=mcp_app.lifespan) -app.mount("/mcp", mcp_app) +app.mount("/mcp", mcp_app) # MCP endpoint at /mcp # Incorrect - missing lifespan app = FastAPI() -app.mount("/mcp", mcp.http_app()) # Session manager won't initialize +app.mount("/mcp", mcp.http_app(path="/")) # Session manager won't initialize ``` If you're mounting an authenticated MCP server under a path prefix, see [Mounting Authenticated Servers](/deployment/http#mounting-authenticated-servers) for important OAuth routing considerations. @@ -427,7 +427,7 @@ async def app_lifespan(app: FastAPI): # Create MCP server mcp = FastMCP("Tools") -mcp_app = mcp.http_app() +mcp_app = mcp.http_app(path="/") # Combine both lifespans app = FastAPI(lifespan=combine_lifespans(app_lifespan, mcp_app.lifespan))