From 9392faaac173864546fd7fc82dcdd656df10d37a Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Fri, 18 Jul 2025 17:13:57 +0100 Subject: [PATCH] docs: clarify streamable_http_path configuration when mounting servers - Add StreamableHTTP servers section mirroring SSE servers structure - Start with simple mounting examples, then explain path composition - Document how to configure streamable_http_path for different scenarios - Add inline comments in Starlette mounting example This addresses confusion around endpoint paths when mounting FastMCP servers as sub-applications in ASGI frameworks. --- README.md | 88 +++++++++++++++++++ .../servers/streamable_http_mounting.py | 72 +++++++++++++++ .../servers/streamable_starlette_mount.py | 5 ++ 3 files changed, 165 insertions(+) create mode 100644 examples/snippets/servers/streamable_http_mounting.py diff --git a/README.md b/README.md index 993b6006b..e07a752a5 100644 --- a/README.md +++ b/README.md @@ -979,6 +979,11 @@ app = Starlette( ], lifespan=lifespan, ) + +# Note: Clients connect to http://localhost:8000/echo/mcp and http://localhost:8000/math/mcp +# To mount at the root of each path (e.g., /echo instead of /echo/mcp): +# echo_mcp.settings.streamable_http_path = "/" +# math_mcp.settings.streamable_http_path = "/" ``` _Full example: [examples/snippets/servers/streamable_starlette_mount.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_starlette_mount.py)_ @@ -1002,6 +1007,89 @@ By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mo For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes). +#### StreamableHTTP servers + +You can mount the StreamableHTTP server to an existing ASGI server using the `streamable_http_app` method. This allows you to integrate the StreamableHTTP server with other ASGI applications. + + +```python +""" +Example showing how to mount StreamableHTTP servers in Starlette applications. + +Run from the repository root: + uvicorn examples.snippets.servers.streamable_http_mounting:app --reload +""" + +from starlette.applications import Starlette +from starlette.routing import Host, Mount + +from mcp.server.fastmcp import FastMCP + +# Basic example - mounting at root +mcp = FastMCP("My App") + + +@mcp.tool() +def hello() -> str: + """A simple hello tool""" + return "Hello from MCP!" + + +# Mount the StreamableHTTP server to the existing ASGI server +app = Starlette( + routes=[ + Mount("/", app=mcp.streamable_http_app()), + ] +) + +# or dynamically mount as host +app.router.routes.append(Host("mcp.acme.corp", app=mcp.streamable_http_app())) + +# Advanced example - multiple servers with path configuration +# Create multiple MCP servers +api_mcp = FastMCP("API Server") +chat_mcp = FastMCP("Chat Server") + + +@api_mcp.tool() +def api_status() -> str: + """Get API status""" + return "API is running" + + +@chat_mcp.tool() +def send_message(message: str) -> str: + """Send a chat message""" + return f"Message sent: {message}" + + +# Default behavior: endpoints will be at /api/mcp and /chat/mcp +default_app = Starlette( + routes=[ + Mount("/api", app=api_mcp.streamable_http_app()), + Mount("/chat", app=chat_mcp.streamable_http_app()), + ] +) + +# To mount at the root of each path (e.g., /api instead of /api/mcp): +# Configure streamable_http_path before mounting +api_mcp.settings.streamable_http_path = "/" +chat_mcp.settings.streamable_http_path = "/" + +configured_app = Starlette( + routes=[ + Mount("/api", app=api_mcp.streamable_http_app()), + Mount("/chat", app=chat_mcp.streamable_http_app()), + ] +) + +# Or configure during initialization +mcp_at_root = FastMCP("My Server", streamable_http_path="/") +``` + +_Full example: [examples/snippets/servers/streamable_http_mounting.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_mounting.py)_ + + #### SSE servers > **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http). diff --git a/examples/snippets/servers/streamable_http_mounting.py b/examples/snippets/servers/streamable_http_mounting.py new file mode 100644 index 000000000..945bdb4c7 --- /dev/null +++ b/examples/snippets/servers/streamable_http_mounting.py @@ -0,0 +1,72 @@ +""" +Example showing how to mount StreamableHTTP servers in Starlette applications. + +Run from the repository root: + uvicorn examples.snippets.servers.streamable_http_mounting:app --reload +""" + +from starlette.applications import Starlette +from starlette.routing import Host, Mount + +from mcp.server.fastmcp import FastMCP + +# Basic example - mounting at root +mcp = FastMCP("My App") + + +@mcp.tool() +def hello() -> str: + """A simple hello tool""" + return "Hello from MCP!" + + +# Mount the StreamableHTTP server to the existing ASGI server +app = Starlette( + routes=[ + Mount("/", app=mcp.streamable_http_app()), + ] +) + +# or dynamically mount as host +app.router.routes.append(Host("mcp.acme.corp", app=mcp.streamable_http_app())) + +# Advanced example - multiple servers with path configuration +# Create multiple MCP servers +api_mcp = FastMCP("API Server") +chat_mcp = FastMCP("Chat Server") + + +@api_mcp.tool() +def api_status() -> str: + """Get API status""" + return "API is running" + + +@chat_mcp.tool() +def send_message(message: str) -> str: + """Send a chat message""" + return f"Message sent: {message}" + + +# Default behavior: endpoints will be at /api/mcp and /chat/mcp +default_app = Starlette( + routes=[ + Mount("/api", app=api_mcp.streamable_http_app()), + Mount("/chat", app=chat_mcp.streamable_http_app()), + ] +) + +# To mount at the root of each path (e.g., /api instead of /api/mcp): +# Configure streamable_http_path before mounting +api_mcp.settings.streamable_http_path = "/" +chat_mcp.settings.streamable_http_path = "/" + +configured_app = Starlette( + routes=[ + Mount("/api", app=api_mcp.streamable_http_app()), + Mount("/chat", app=chat_mcp.streamable_http_app()), + ] +) + +# Or configure during initialization +mcp_at_root = FastMCP("My Server", streamable_http_path="/") diff --git a/examples/snippets/servers/streamable_starlette_mount.py b/examples/snippets/servers/streamable_starlette_mount.py index 19e41294b..57d2d2ea5 100644 --- a/examples/snippets/servers/streamable_starlette_mount.py +++ b/examples/snippets/servers/streamable_starlette_mount.py @@ -47,3 +47,8 @@ async def lifespan(app: Starlette): ], lifespan=lifespan, ) + +# Note: Clients connect to http://localhost:8000/echo/mcp and http://localhost:8000/math/mcp +# To mount at the root of each path (e.g., /echo instead of /echo/mcp): +# echo_mcp.settings.streamable_http_path = "/" +# math_mcp.settings.streamable_http_path = "/"