Skip to content

Commit a3cfc48

Browse files
authored
Merge branch 'main' into show-server-info
2 parents 96f3cf2 + a9aca20 commit a3cfc48

File tree

3 files changed

+47
-19
lines changed

3 files changed

+47
-19
lines changed

README.md

Lines changed: 26 additions & 0 deletions
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/cli/cli.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,15 +294,14 @@ def run(
294294
) -> None:
295295
"""Run a MCP server.
296296
297-
The server can be specified in two ways:
298-
1. Module approach: server.py - runs the module directly, expecting a server.run()
299-
call
300-
2. Import approach: server.py:app - imports and runs the specified server object
297+
The server can be specified in two ways:\n
298+
1. Module approach: server.py - runs the module directly, expecting a server.run() call.\n
299+
2. Import approach: server.py:app - imports and runs the specified server object.\n\n
301300
302301
Note: This command runs the server directly. You are responsible for ensuring
303-
all dependencies are available. For dependency management, use mcp install
304-
or mcp dev instead.
305-
"""
302+
all dependencies are available.\n
303+
For dependency management, use `mcp install` or `mcp dev` instead.
304+
""" # noqa: E501
306305
file, server_object = _parse_file_path(file_spec)
307306

308307
logger.debug(

src/mcp/server/fastmcp/server.py

Lines changed: 15 additions & 12 deletions
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
@@ -483,9 +485,19 @@ async def run_stdio_async(self) -> None:
483485

484486
async def run_sse_async(self) -> None:
485487
"""Run the server using SSE transport."""
486-
from starlette.applications import Starlette
487-
from starlette.routing import Mount, Route
488+
starlette_app = self.sse_app()
488489

490+
config = uvicorn.Config(
491+
starlette_app,
492+
host=self.settings.host,
493+
port=self.settings.port,
494+
log_level=self.settings.log_level.lower(),
495+
)
496+
server = uvicorn.Server(config)
497+
await server.serve()
498+
499+
def sse_app(self) -> Starlette:
500+
"""Return an instance of the SSE server app."""
489501
sse = SseServerTransport("/messages/")
490502

491503
async def handle_sse(request):
@@ -498,23 +510,14 @@ async def handle_sse(request):
498510
self._mcp_server.create_initialization_options(),
499511
)
500512

501-
starlette_app = Starlette(
513+
return Starlette(
502514
debug=self.settings.debug,
503515
routes=[
504516
Route("/sse", endpoint=handle_sse),
505517
Mount("/messages/", app=sse.handle_post_message),
506518
],
507519
)
508520

509-
config = uvicorn.Config(
510-
starlette_app,
511-
host=self.settings.host,
512-
port=self.settings.port,
513-
log_level=self.settings.log_level.lower(),
514-
)
515-
server = uvicorn.Server(config)
516-
await server.serve()
517-
518521
async def list_prompts(self) -> list[MCPPrompt]:
519522
"""List all available prompts."""
520523
prompts = self._prompt_manager.list_prompts()

0 commit comments

Comments
 (0)