Skip to content

Commit cb85e20

Browse files
committed
streamable http examples
1 parent d6a51f7 commit cb85e20

File tree

6 files changed

+211
-49
lines changed

6 files changed

+211
-49
lines changed

README.md

Lines changed: 78 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -863,72 +863,102 @@ Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMC
863863

864864
> **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
865865
866+
<!-- snippet-source examples/snippets/servers/streamable_config.py -->
866867
```python
868+
"""Streamable HTTP server configuration examples.
869+
870+
This example shows different configuration options for
871+
streamable HTTP servers.
872+
873+
Run from the repository root:
874+
uv run examples/snippets/servers/streamable_config.py
875+
"""
876+
867877
from mcp.server.fastmcp import FastMCP
868878

869879
# Stateful server (maintains session state)
870880
mcp = FastMCP("StatefulServer")
871881

882+
# Other configuration options:
872883
# Stateless server (no session persistence)
873-
mcp = FastMCP("StatelessServer", stateless_http=True)
884+
# mcp = FastMCP("StatelessServer", stateless_http=True)
874885

875886
# Stateless server (no session persistence, no sse stream with supported client)
876-
mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
887+
# mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
888+
889+
890+
# Add a simple tool to demonstrate the server
891+
@mcp.tool()
892+
def greet(name: str = "World") -> str:
893+
"""Greet someone by name."""
894+
return f"Hello, {name}!"
895+
877896

878897
# Run server with streamable_http transport
879-
mcp.run(transport="streamable-http")
898+
if __name__ == "__main__":
899+
mcp.run(transport="streamable-http")
880900
```
881901

902+
_Full example: [examples/snippets/servers/streamable_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_config.py)_
903+
<!-- /snippet-source -->
904+
882905
You can mount multiple FastMCP servers in a FastAPI application:
883906

907+
<!-- snippet-source examples/snippets/servers/streamable_fastapi_mount.py -->
884908
```python
885-
# echo.py
909+
"""Example of mounting multiple FastMCP servers in a FastAPI application.
910+
911+
This example shows how to create multiple MCP servers and mount them
912+
at different endpoints in a single FastAPI application.
913+
914+
Run from the repository root:
915+
uvicorn examples.snippets.servers.streamable_fastapi_mount:app --reload
916+
"""
917+
918+
import contextlib
919+
920+
from fastapi import FastAPI
921+
886922
from mcp.server.fastmcp import FastMCP
887923

888-
mcp = FastMCP(name="EchoServer", stateless_http=True)
924+
# Create the Echo server
925+
echo_mcp = FastMCP(name="EchoServer", stateless_http=True)
889926

890927

891-
@mcp.tool()
928+
@echo_mcp.tool()
892929
def echo(message: str) -> str:
893930
"""A simple echo tool"""
894931
return f"Echo: {message}"
895-
```
896932

897-
```python
898-
# math.py
899-
from mcp.server.fastmcp import FastMCP
900933

901-
mcp = FastMCP(name="MathServer", stateless_http=True)
934+
# Create the Math server
935+
math_mcp = FastMCP(name="MathServer", stateless_http=True)
902936

903937

904-
@mcp.tool()
938+
@math_mcp.tool()
905939
def add_two(n: int) -> int:
906940
"""Tool to add two to the input"""
907941
return n + 2
908-
```
909-
910-
```python
911-
# main.py
912-
import contextlib
913-
from fastapi import FastAPI
914-
from mcp.echo import echo
915-
from mcp.math import math
916942

917943

918944
# Create a combined lifespan to manage both session managers
919945
@contextlib.asynccontextmanager
920946
async def lifespan(app: FastAPI):
921947
async with contextlib.AsyncExitStack() as stack:
922-
await stack.enter_async_context(echo.mcp.session_manager.run())
923-
await stack.enter_async_context(math.mcp.session_manager.run())
948+
await stack.enter_async_context(echo_mcp.session_manager.run())
949+
await stack.enter_async_context(math_mcp.session_manager.run())
924950
yield
925951

926952

953+
# Create the FastAPI app and mount the MCP servers
927954
app = FastAPI(lifespan=lifespan)
928-
app.mount("/echo", echo.mcp.streamable_http_app())
929-
app.mount("/math", math.mcp.streamable_http_app())
955+
app.mount("/echo", echo_mcp.streamable_http_app())
956+
app.mount("/math", math_mcp.streamable_http_app())
930957
```
931958

959+
_Full example: [examples/snippets/servers/streamable_fastapi_mount.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_fastapi_mount.py)_
960+
<!-- /snippet-source -->
961+
932962
For low level server with Streamable HTTP implementations, see:
933963

934964
- Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/)
@@ -945,26 +975,6 @@ The streamable HTTP transport supports:
945975

946976
By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mounted at `/mcp`. You can customize these paths using the methods described below.
947977

948-
#### Streamable HTTP servers
949-
950-
The following example shows how to use `streamable_http_app()`, a method that returns a `Starlette` application object.
951-
You can then append additional routes to that application as needed.
952-
953-
```python
954-
mcp = FastMCP("My App")
955-
956-
app = mcp.streamable_http_app()
957-
# Additional non-MCP routes can be added like so:
958-
# from starlette.routing import Route
959-
# app.router.routes.append(Route("/", endpoint=other_route_function))
960-
```
961-
962-
To customize the route from the default of "/mcp", either specify the `streamable_http_path` option for the `FastMCP` constructor,
963-
or set `FASTMCP_STREAMABLE_HTTP_PATH` environment variable.
964-
965-
Note that in Starlette and FastAPI (which is based on Starlette), the "/mcp" route will redirect to "/mcp/",
966-
so you may need to use "/mcp/" when pointing MCP clients at your servers.
967-
968978
For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
969979

970980
#### SSE servers
@@ -1437,14 +1447,26 @@ _Full example: [examples/snippets/clients/stdio_client.py](https://github.com/mo
14371447

14381448
Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):
14391449

1450+
<!-- snippet-source examples/snippets/clients/streamable_basic.py -->
14401451
```python
1441-
from mcp.client.streamable_http import streamablehttp_client
1452+
"""Basic streamable HTTP client example.
1453+
1454+
This example shows the minimal code needed to connect to
1455+
a streamable HTTP server and call a tool.
1456+
1457+
Run from the repository root:
1458+
uv run examples/snippets/clients/streamable_basic.py
1459+
"""
1460+
1461+
import asyncio
1462+
14421463
from mcp import ClientSession
1464+
from mcp.client.streamable_http import streamablehttp_client
14431465

14441466

14451467
async def main():
14461468
# Connect to a streamable HTTP server
1447-
async with streamablehttp_client("example/mcp") as (
1469+
async with streamablehttp_client("http://localhost:8000/mcp") as (
14481470
read_stream,
14491471
write_stream,
14501472
_,
@@ -1453,10 +1475,18 @@ async def main():
14531475
async with ClientSession(read_stream, write_stream) as session:
14541476
# Initialize the connection
14551477
await session.initialize()
1456-
# Call a tool
1457-
tool_result = await session.call_tool("echo", {"message": "hello"})
1478+
# List available tools
1479+
tools = await session.list_tools()
1480+
print(f"Available tools: {[tool.name for tool in tools.tools]}")
1481+
1482+
1483+
if __name__ == "__main__":
1484+
asyncio.run(main())
14581485
```
14591486

1487+
_Full example: [examples/snippets/clients/streamable_basic.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/streamable_basic.py)_
1488+
<!-- /snippet-source -->
1489+
14601490
### Client Display Utilities
14611491

14621492
When building MCP clients, the SDK provides utilities to help display human-readable names for tools, resources, and prompts:
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Basic streamable HTTP client example.
2+
3+
This example shows the minimal code needed to connect to
4+
a streamable HTTP server and call a tool.
5+
6+
Run from the repository root:
7+
uv run examples/snippets/clients/streamable_basic.py
8+
"""
9+
10+
import asyncio
11+
12+
from mcp import ClientSession
13+
from mcp.client.streamable_http import streamablehttp_client
14+
15+
16+
async def main():
17+
# Connect to a streamable HTTP server
18+
async with streamablehttp_client("http://localhost:8000/mcp") as (
19+
read_stream,
20+
write_stream,
21+
_,
22+
):
23+
# Create a session using the client streams
24+
async with ClientSession(read_stream, write_stream) as session:
25+
# Initialize the connection
26+
await session.initialize()
27+
# List available tools
28+
tools = await session.list_tools()
29+
print(f"Available tools: {[tool.name for tool in tools.tools]}")
30+
31+
32+
if __name__ == "__main__":
33+
asyncio.run(main())

examples/snippets/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ description = "MCP Example Snippets"
55
requires-python = ">=3.10"
66
dependencies = [
77
"mcp",
8+
"fastapi",
89
]
910

1011
[build-system]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Streamable HTTP server configuration examples.
2+
3+
This example shows different configuration options for
4+
streamable HTTP servers.
5+
6+
Run from the repository root:
7+
uv run examples/snippets/servers/streamable_config.py
8+
"""
9+
10+
from mcp.server.fastmcp import FastMCP
11+
12+
# Stateful server (maintains session state)
13+
mcp = FastMCP("StatefulServer")
14+
15+
# Other configuration options:
16+
# Stateless server (no session persistence)
17+
# mcp = FastMCP("StatelessServer", stateless_http=True)
18+
19+
# Stateless server (no session persistence, no sse stream with supported client)
20+
# mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
21+
22+
23+
# Add a simple tool to demonstrate the server
24+
@mcp.tool()
25+
def greet(name: str = "World") -> str:
26+
"""Greet someone by name."""
27+
return f"Hello, {name}!"
28+
29+
30+
# Run server with streamable_http transport
31+
if __name__ == "__main__":
32+
mcp.run(transport="streamable-http")
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Example of mounting multiple FastMCP servers in a FastAPI application.
2+
3+
This example shows how to create multiple MCP servers and mount them
4+
at different endpoints in a single FastAPI application.
5+
6+
Run from the repository root:
7+
uvicorn examples.snippets.servers.streamable_fastapi_mount:app --reload
8+
"""
9+
10+
import contextlib
11+
12+
from fastapi import FastAPI
13+
14+
from mcp.server.fastmcp import FastMCP
15+
16+
# Create the Echo server
17+
echo_mcp = FastMCP(name="EchoServer", stateless_http=True)
18+
19+
20+
@echo_mcp.tool()
21+
def echo(message: str) -> str:
22+
"""A simple echo tool"""
23+
return f"Echo: {message}"
24+
25+
26+
# Create the Math server
27+
math_mcp = FastMCP(name="MathServer", stateless_http=True)
28+
29+
30+
@math_mcp.tool()
31+
def add_two(n: int) -> int:
32+
"""Tool to add two to the input"""
33+
return n + 2
34+
35+
36+
# Create a combined lifespan to manage both session managers
37+
@contextlib.asynccontextmanager
38+
async def lifespan(app: FastAPI):
39+
async with contextlib.AsyncExitStack() as stack:
40+
await stack.enter_async_context(echo_mcp.session_manager.run())
41+
await stack.enter_async_context(math_mcp.session_manager.run())
42+
yield
43+
44+
45+
# Create the FastAPI app and mount the MCP servers
46+
app = FastAPI(lifespan=lifespan)
47+
app.mount("/echo", echo_mcp.streamable_http_app())
48+
app.mount("/math", math_mcp.streamable_http_app())

uv.lock

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)