Skip to content

docs: clarify streamable_http_path configuration when mounting servers #1172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)_
Expand All @@ -1002,6 +1007,172 @@ 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.

##### Basic mounting

<!-- snippet-source examples/snippets/servers/streamable_http_basic_mounting.py -->
```python
"""
Basic example showing how to mount StreamableHTTP server in Starlette.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Mount

from mcp.server.fastmcp import FastMCP

# Create MCP server
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()),
]
)
```

_Full example: [examples/snippets/servers/streamable_http_basic_mounting.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_basic_mounting.py)_
<!-- /snippet-source -->

##### Host-based routing

<!-- snippet-source examples/snippets/servers/streamable_http_host_mounting.py -->
```python
"""
Example showing how to mount StreamableHTTP server using Host-based routing.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_host_mounting:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Host

from mcp.server.fastmcp import FastMCP

# Create MCP server
mcp = FastMCP("MCP Host App")


@mcp.tool()
def domain_info() -> str:
"""Get domain-specific information"""
return "This is served from mcp.acme.corp"


# Mount using Host-based routing
app = Starlette(
routes=[
Host("mcp.acme.corp", app=mcp.streamable_http_app()),
]
)
```

_Full example: [examples/snippets/servers/streamable_http_host_mounting.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_host_mounting.py)_
<!-- /snippet-source -->

##### Multiple servers with path configuration

<!-- snippet-source examples/snippets/servers/streamable_http_multiple_servers.py -->
```python
"""
Example showing how to mount multiple StreamableHTTP servers with path configuration.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_multiple_servers:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Mount

from mcp.server.fastmcp import FastMCP

# 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}"


# Configure servers to mount at the root of each path
# This means endpoints will be at /api and /chat instead of /api/mcp and /chat/mcp
api_mcp.settings.streamable_http_path = "/"
chat_mcp.settings.streamable_http_path = "/"

# Mount the servers
app = Starlette(
routes=[
Mount("/api", app=api_mcp.streamable_http_app()),
Mount("/chat", app=chat_mcp.streamable_http_app()),
]
)
```

_Full example: [examples/snippets/servers/streamable_http_multiple_servers.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_multiple_servers.py)_
<!-- /snippet-source -->

##### Path configuration at initialization

<!-- snippet-source examples/snippets/servers/streamable_http_path_config.py -->
```python
"""
Example showing path configuration during FastMCP initialization.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_path_config:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Mount

from mcp.server.fastmcp import FastMCP

# Configure streamable_http_path during initialization
# This server will mount at the root of wherever it's mounted
mcp_at_root = FastMCP("My Server", streamable_http_path="/")


@mcp_at_root.tool()
def process_data(data: str) -> str:
"""Process some data"""
return f"Processed: {data}"


# Mount at /process - endpoints will be at /process instead of /process/mcp
app = Starlette(
routes=[
Mount("/process", app=mcp_at_root.streamable_http_app()),
]
)
```

_Full example: [examples/snippets/servers/streamable_http_path_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_path_config.py)_
<!-- /snippet-source -->

#### SSE servers

> **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).
Expand Down
28 changes: 28 additions & 0 deletions examples/snippets/servers/streamable_http_basic_mounting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Basic example showing how to mount StreamableHTTP server in Starlette.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Mount

from mcp.server.fastmcp import FastMCP

# Create MCP server
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()),
]
)
28 changes: 28 additions & 0 deletions examples/snippets/servers/streamable_http_host_mounting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Example showing how to mount StreamableHTTP server using Host-based routing.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_host_mounting:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Host

from mcp.server.fastmcp import FastMCP

# Create MCP server
mcp = FastMCP("MCP Host App")


@mcp.tool()
def domain_info() -> str:
"""Get domain-specific information"""
return "This is served from mcp.acme.corp"


# Mount using Host-based routing
app = Starlette(
routes=[
Host("mcp.acme.corp", app=mcp.streamable_http_app()),
]
)
41 changes: 41 additions & 0 deletions examples/snippets/servers/streamable_http_multiple_servers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
Example showing how to mount multiple StreamableHTTP servers with path configuration.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_multiple_servers:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Mount

from mcp.server.fastmcp import FastMCP

# 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}"


# Configure servers to mount at the root of each path
# This means endpoints will be at /api and /chat instead of /api/mcp and /chat/mcp
api_mcp.settings.streamable_http_path = "/"
chat_mcp.settings.streamable_http_path = "/"

# Mount the servers
app = Starlette(
routes=[
Mount("/api", app=api_mcp.streamable_http_app()),
Mount("/chat", app=chat_mcp.streamable_http_app()),
]
)
29 changes: 29 additions & 0 deletions examples/snippets/servers/streamable_http_path_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Example showing path configuration during FastMCP initialization.

Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_path_config:app --reload
"""

from starlette.applications import Starlette
from starlette.routing import Mount

from mcp.server.fastmcp import FastMCP

# Configure streamable_http_path during initialization
# This server will mount at the root of wherever it's mounted
mcp_at_root = FastMCP("My Server", streamable_http_path="/")


@mcp_at_root.tool()
def process_data(data: str) -> str:
"""Process some data"""
return f"Processed: {data}"


# Mount at /process - endpoints will be at /process instead of /process/mcp
app = Starlette(
routes=[
Mount("/process", app=mcp_at_root.streamable_http_app()),
]
)
5 changes: 5 additions & 0 deletions examples/snippets/servers/streamable_starlette_mount.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "/"
Loading