Add lifespan property to app

This commit is contained in:
Jeremiah Lowin 2025-05-16 10:51:48 -04:00
commit c3e9bdb49e
4 changed files with 23 additions and 16 deletions

View file

@ -124,7 +124,7 @@ app = Starlette(
Mount("/mcp-server", app=mcp_app),
# Add other routes as needed
],
lifespan=mcp_app.router.lifespan_context,
lifespan=mcp_app.lifespan,
)
```
@ -154,7 +154,7 @@ mcp_app = mcp.http_app(path='/mcp')
inner_app = Starlette(routes=[Mount("/inner", app=mcp_app)])
app = Starlette(
routes=[Mount("/outer", app=inner_app)],
lifespan=mcp_app.router.lifespan_context,
lifespan=mcp_app.lifespan,
)
```
@ -181,7 +181,7 @@ mcp = FastMCP("MyServer")
mcp_app = mcp.http_app(path='/mcp')
# Create a FastAPI app and mount the MCP server
app = FastAPI(lifespan=mcp_app.router.lifespan_context)
app = FastAPI(lifespan=mcp_app.lifespan)
app.mount("/mcp-server", mcp_app)
```

View file

@ -27,7 +27,7 @@ from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.requests import Request
from starlette.responses import Response
from starlette.routing import BaseRoute, Mount, Route
from starlette.types import Receive, Scope, Send
from starlette.types import Lifespan, Receive, Scope, Send
from fastmcp.utilities.logging import get_logger
@ -43,6 +43,12 @@ _current_http_request: ContextVar[Request | None] = ContextVar(
)
class StarletteWithLifespan(Starlette):
@property
def lifespan(self) -> Lifespan:
return self.router.lifespan_context
@contextmanager
def set_http_request(request: Request) -> Generator[Request, None, None]:
token = _current_http_request.set(request)
@ -122,7 +128,7 @@ def create_base_app(
middleware: list[Middleware],
debug: bool = False,
lifespan: Callable | None = None,
) -> Starlette:
) -> StarletteWithLifespan:
"""Create a base Starlette app with common middleware and routes.
Args:
@ -137,7 +143,7 @@ def create_base_app(
# Always add RequestContextMiddleware as the outermost middleware
middleware.append(Middleware(RequestContextMiddleware))
return Starlette(
return StarletteWithLifespan(
routes=routes,
middleware=middleware,
debug=debug,
@ -157,7 +163,7 @@ def create_sse_app(
debug: bool = False,
routes: list[BaseRoute] | None = None,
middleware: list[Middleware] | None = None,
) -> Starlette:
) -> StarletteWithLifespan:
"""Return an instance of the SSE server app.
Args:
@ -262,7 +268,7 @@ def create_streamable_http_app(
debug: bool = False,
routes: list[BaseRoute] | None = None,
middleware: list[Middleware] | None = None,
) -> Starlette:
) -> StarletteWithLifespan:
"""Return an instance of the StreamableHTTP server app.
Args:

View file

@ -35,7 +35,6 @@ from mcp.types import Resource as MCPResource
from mcp.types import ResourceTemplate as MCPResourceTemplate
from mcp.types import Tool as MCPTool
from pydantic import AnyUrl
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.requests import Request
from starlette.responses import Response
@ -48,7 +47,11 @@ from fastmcp.prompts import Prompt, PromptManager
from fastmcp.prompts.prompt import PromptResult
from fastmcp.resources import Resource, ResourceManager
from fastmcp.resources.template import ResourceTemplate
from fastmcp.server.http import create_sse_app
from fastmcp.server.http import (
StarletteWithLifespan,
create_sse_app,
create_streamable_http_app,
)
from fastmcp.tools import ToolManager
from fastmcp.tools.tool import Tool
from fastmcp.utilities.cache import TimedCache
@ -59,7 +62,6 @@ if TYPE_CHECKING:
from fastmcp.client import Client
from fastmcp.server.openapi import FastMCPOpenAPI
from fastmcp.server.proxy import FastMCPProxy
logger = get_logger(__name__)
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
@ -806,7 +808,7 @@ class FastMCP(Generic[LifespanResultT]):
path: str | None = None,
message_path: str | None = None,
middleware: list[Middleware] | None = None,
) -> Starlette:
) -> StarletteWithLifespan:
"""
Create a Starlette app for the SSE server.
@ -837,7 +839,7 @@ class FastMCP(Generic[LifespanResultT]):
self,
path: str | None = None,
middleware: list[Middleware] | None = None,
) -> Starlette:
) -> StarletteWithLifespan:
"""
Create a Starlette app for the StreamableHTTP server.
@ -858,7 +860,7 @@ class FastMCP(Generic[LifespanResultT]):
path: str | None = None,
middleware: list[Middleware] | None = None,
transport: Literal["streamable-http", "sse"] = "streamable-http",
) -> Starlette:
) -> StarletteWithLifespan:
"""Create a Starlette app using the specified HTTP transport.
Args:
@ -869,7 +871,6 @@ class FastMCP(Generic[LifespanResultT]):
Returns:
A Starlette application configured with the specified transport
"""
from fastmcp.server.http import create_streamable_http_app
if transport == "streamable-http":
return create_streamable_http_app(

View file

@ -119,7 +119,7 @@ def run_nested_server(host: str, port: int) -> None:
mount = Starlette(routes=[Mount("/nest-inner", app=mcp_app)])
mount2 = Starlette(
routes=[Mount("/nest-outer", app=mount)],
lifespan=mcp_app.router.lifespan_context,
lifespan=mcp_app.lifespan,
)
server = uvicorn.Server(
config=uvicorn.Config(