Skip to content

Commit a9aca20

Browse files
authoredMar 19, 2025··
Option to mount SSE server to existing ASGI server (#312)
* Option to mount SSE server to existing ASGI server Fixes #311 Add option to mount SSE server to an existing ASGI server. * Add a new method `sse_app` in `src/mcp/server/fastmcp/server.py` to return an instance of the SSE server app. * Update the `run_sse_async` method in `src/mcp/server/fastmcp/server.py` to use the new `sse_app` method. * Update the documentation in `README.md` to include instructions on how to mount the SSE server to an existing ASGI server. * Fix the example in `README.md` to use `app.mount('/', mcp.sse_app())` instead. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/modelcontextprotocol/python-sdk/issues/311?shareId=XXXX-XXXX-XXXX-XXXX). * Add `sse_app` method and update `run_sse_async` method in `server.py` * Add `sse_app` method to return an instance of the SSE server app * Update `run_sse_async` method to use the new `sse_app` method Update `README.md` to include instructions for mounting SSE server * Add section on mounting the SSE server to an existing ASGI server * fix: Move import statements to the top of the file/ * docs: Update README to reflect changes in mounting SSE server with Starlette * docs: Formatting of SSE server mounting example in README
1 parent 6b6f34e commit a9aca20

File tree

2 files changed

+41
-12
lines changed

2 files changed

+41
-12
lines changed
 

‎README.md

+26
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
- [Development Mode](#development-mode)
3232
- [Claude Desktop Integration](#claude-desktop-integration)
3333
- [Direct Execution](#direct-execution)
34+
- [Mounting to an Existing ASGI Server](#mounting-to-an-existing-asgi-server)
3435
- [Examples](#examples)
3536
- [Echo Server](#echo-server)
3637
- [SQLite Explorer](#sqlite-explorer)
@@ -346,6 +347,31 @@ python server.py
346347
mcp run server.py
347348
```
348349

350+
### Mounting to an Existing ASGI Server
351+
352+
You can mount the SSE server to an existing ASGI server using the `sse_app` method. This allows you to integrate the SSE server with other ASGI applications.
353+
354+
```python
355+
from starlette.applications import Starlette
356+
from starlette.routes import Mount, Host
357+
from mcp.server.fastmcp import FastMCP
358+
359+
360+
mcp = FastMCP("My App")
361+
362+
# Mount the SSE server to the existing ASGI server
363+
app = Starlette(
364+
routes=[
365+
Mount('/', app=mcp.sse_app()),
366+
]
367+
)
368+
369+
# or dynamically mount as host
370+
app.router.routes.append(Host('mcp.acme.corp', app=mcp.sse_app()))
371+
```
372+
373+
For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
374+
349375
## Examples
350376

351377
### Echo Server

‎src/mcp/server/fastmcp/server.py

+15-12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from pydantic import BaseModel, Field
2020
from pydantic.networks import AnyUrl
2121
from pydantic_settings import BaseSettings, SettingsConfigDict
22+
from starlette.applications import Starlette
23+
from starlette.routing import Mount, Route
2224

2325
from mcp.server.fastmcp.exceptions import ResourceError
2426
from mcp.server.fastmcp.prompts import Prompt, PromptManager
@@ -461,9 +463,19 @@ async def run_stdio_async(self) -> None:
461463

462464
async def run_sse_async(self) -> None:
463465
"""Run the server using SSE transport."""
464-
from starlette.applications import Starlette
465-
from starlette.routing import Mount, Route
466+
starlette_app = self.sse_app()
466467

468+
config = uvicorn.Config(
469+
starlette_app,
470+
host=self.settings.host,
471+
port=self.settings.port,
472+
log_level=self.settings.log_level.lower(),
473+
)
474+
server = uvicorn.Server(config)
475+
await server.serve()
476+
477+
def sse_app(self) -> Starlette:
478+
"""Return an instance of the SSE server app."""
467479
sse = SseServerTransport("/messages/")
468480

469481
async def handle_sse(request):
@@ -476,23 +488,14 @@ async def handle_sse(request):
476488
self._mcp_server.create_initialization_options(),
477489
)
478490

479-
starlette_app = Starlette(
491+
return Starlette(
480492
debug=self.settings.debug,
481493
routes=[
482494
Route("/sse", endpoint=handle_sse),
483495
Mount("/messages/", app=sse.handle_post_message),
484496
],
485497
)
486498

487-
config = uvicorn.Config(
488-
starlette_app,
489-
host=self.settings.host,
490-
port=self.settings.port,
491-
log_level=self.settings.log_level.lower(),
492-
)
493-
server = uvicorn.Server(config)
494-
await server.serve()
495-
496499
async def list_prompts(self) -> list[MCPPrompt]:
497500
"""List all available prompts."""
498501
prompts = self._prompt_manager.list_prompts()

0 commit comments

Comments
 (0)
Please sign in to comment.