Skip to content

Commit 0f7b167

Browse files
committed
expose session_manager in FastMCP
1 parent 3d8ff7a commit 0f7b167

File tree

1 file changed

+30
-10
lines changed

1 file changed

+30
-10
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def __init__(
175175
self._event_store = event_store
176176
self._custom_starlette_routes: list[Route] = []
177177
self.dependencies = self.settings.dependencies
178+
self._session_manager: StreamableHTTPSessionManager | None = None
178179

179180
# Set up MCP protocol handlers
180181
self._setup_handlers()
@@ -190,6 +191,25 @@ def name(self) -> str:
190191
def instructions(self) -> str | None:
191192
return self._mcp_server.instructions
192193

194+
@property
195+
def session_manager(self) -> StreamableHTTPSessionManager:
196+
"""Get the StreamableHTTP session manager.
197+
198+
This is exposed to enable advanced use cases like mounting multiple
199+
FastMCP servers in a single FastAPI application.
200+
201+
Raises:
202+
RuntimeError: If called before streamable_http_app() has been called.
203+
"""
204+
if self._session_manager is None:
205+
raise RuntimeError(
206+
"Session manager can only be accessed after"
207+
"calling streamable_http_app()."
208+
"The session manager is created lazily"
209+
"to avoid unnecessary initialization."
210+
)
211+
return self._session_manager
212+
193213
def run(
194214
self,
195215
transport: Literal["stdio", "sse", "streamable-http"] = "stdio",
@@ -746,19 +766,20 @@ def streamable_http_app(self) -> Starlette:
746766
from starlette.middleware import Middleware
747767
from starlette.routing import Mount
748768

749-
# Create session manager using the provided event store
750-
session_manager = StreamableHTTPSessionManager(
751-
app=self._mcp_server,
752-
event_store=self._event_store,
753-
json_response=self.settings.json_response,
754-
stateless=self.settings.stateless_http, # Use the stateless setting
755-
)
769+
# Create session manager on first call (lazy initialization)
770+
if self._session_manager is None:
771+
self._session_manager = StreamableHTTPSessionManager(
772+
app=self._mcp_server,
773+
event_store=self._event_store,
774+
json_response=self.settings.json_response,
775+
stateless=self.settings.stateless_http, # Use the stateless setting
776+
)
756777

757778
# Create the ASGI handler
758779
async def handle_streamable_http(
759780
scope: Scope, receive: Receive, send: Send
760781
) -> None:
761-
await session_manager.handle_request(scope, receive, send)
782+
await self.session_manager.handle_request(scope, receive, send)
762783

763784
# Create routes
764785
routes: list[Route | Mount] = []
@@ -807,12 +828,11 @@ async def handle_streamable_http(
807828

808829
routes.extend(self._custom_starlette_routes)
809830

810-
# Create Starlette app with routes and middleware
811831
return Starlette(
812832
debug=self.settings.debug,
813833
routes=routes,
814834
middleware=middleware,
815-
lifespan=lambda app: session_manager.run(),
835+
lifespan=lambda app: self.session_manager.run(),
816836
)
817837

818838
async def list_prompts(self) -> list[MCPPrompt]:

0 commit comments

Comments
 (0)