Skip to content

Commit 9f14d04

Browse files
Merge pull request #1 from ranjitodedra/fix/streamable-http-mounting-lifespan
Extend Fix/streamable http mounting lifespan
2 parents 64ee2a9 + 0d13817 commit 9f14d04

File tree

5 files changed

+112
-18
lines changed

5 files changed

+112
-18
lines changed

README.md

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,13 +1317,15 @@ Run from the repository root:
13171317
uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
13181318
"""
13191319

1320+
import contextlib
1321+
13201322
from starlette.applications import Starlette
13211323
from starlette.routing import Mount
13221324

13231325
from mcp.server.fastmcp import FastMCP
13241326

13251327
# Create MCP server
1326-
mcp = FastMCP("My App")
1328+
mcp = FastMCP("My App", stateless_http=True)
13271329

13281330

13291331
@mcp.tool()
@@ -1332,11 +1334,20 @@ def hello() -> str:
13321334
return "Hello from MCP!"
13331335

13341336

1337+
# Create lifespan context manager to initialize the session manager
1338+
@contextlib.asynccontextmanager
1339+
async def lifespan(app: Starlette):
1340+
"""Context manager for managing MCP session manager lifecycle."""
1341+
async with mcp.session_manager.run():
1342+
yield
1343+
1344+
13351345
# Mount the StreamableHTTP server to the existing ASGI server
13361346
app = Starlette(
13371347
routes=[
13381348
Mount("/", app=mcp.streamable_http_app()),
1339-
]
1349+
],
1350+
lifespan=lifespan,
13401351
)
13411352
```
13421353

@@ -1354,13 +1365,15 @@ Run from the repository root:
13541365
uvicorn examples.snippets.servers.streamable_http_host_mounting:app --reload
13551366
"""
13561367

1368+
import contextlib
1369+
13571370
from starlette.applications import Starlette
13581371
from starlette.routing import Host
13591372

13601373
from mcp.server.fastmcp import FastMCP
13611374

13621375
# Create MCP server
1363-
mcp = FastMCP("MCP Host App")
1376+
mcp = FastMCP("MCP Host App", stateless_http=True)
13641377

13651378

13661379
@mcp.tool()
@@ -1369,11 +1382,20 @@ def domain_info() -> str:
13691382
return "This is served from mcp.acme.corp"
13701383

13711384

1385+
# Create lifespan context manager to initialize the session manager
1386+
@contextlib.asynccontextmanager
1387+
async def lifespan(app: Starlette):
1388+
"""Context manager for managing MCP session manager lifecycle."""
1389+
async with mcp.session_manager.run():
1390+
yield
1391+
1392+
13721393
# Mount using Host-based routing
13731394
app = Starlette(
13741395
routes=[
13751396
Host("mcp.acme.corp", app=mcp.streamable_http_app()),
1376-
]
1397+
],
1398+
lifespan=lifespan,
13771399
)
13781400
```
13791401

@@ -1391,14 +1413,16 @@ Run from the repository root:
13911413
uvicorn examples.snippets.servers.streamable_http_multiple_servers:app --reload
13921414
"""
13931415

1416+
import contextlib
1417+
13941418
from starlette.applications import Starlette
13951419
from starlette.routing import Mount
13961420

13971421
from mcp.server.fastmcp import FastMCP
13981422

13991423
# Create multiple MCP servers
1400-
api_mcp = FastMCP("API Server")
1401-
chat_mcp = FastMCP("Chat Server")
1424+
api_mcp = FastMCP("API Server", stateless_http=True)
1425+
chat_mcp = FastMCP("Chat Server", stateless_http=True)
14021426

14031427

14041428
@api_mcp.tool()
@@ -1418,12 +1442,24 @@ def send_message(message: str) -> str:
14181442
api_mcp.settings.streamable_http_path = "/"
14191443
chat_mcp.settings.streamable_http_path = "/"
14201444

1445+
1446+
# Create lifespan context manager to initialize both session managers
1447+
@contextlib.asynccontextmanager
1448+
async def lifespan(app: Starlette):
1449+
"""Context manager for managing multiple MCP session managers."""
1450+
async with contextlib.AsyncExitStack() as stack:
1451+
await stack.enter_async_context(api_mcp.session_manager.run())
1452+
await stack.enter_async_context(chat_mcp.session_manager.run())
1453+
yield
1454+
1455+
14211456
# Mount the servers
14221457
app = Starlette(
14231458
routes=[
14241459
Mount("/api", app=api_mcp.streamable_http_app()),
14251460
Mount("/chat", app=chat_mcp.streamable_http_app()),
1426-
]
1461+
],
1462+
lifespan=lifespan,
14271463
)
14281464
```
14291465

@@ -1441,14 +1477,16 @@ Run from the repository root:
14411477
uvicorn examples.snippets.servers.streamable_http_path_config:app --reload
14421478
"""
14431479

1480+
import contextlib
1481+
14441482
from starlette.applications import Starlette
14451483
from starlette.routing import Mount
14461484

14471485
from mcp.server.fastmcp import FastMCP
14481486

14491487
# Configure streamable_http_path during initialization
14501488
# This server will mount at the root of wherever it's mounted
1451-
mcp_at_root = FastMCP("My Server", streamable_http_path="/")
1489+
mcp_at_root = FastMCP("My Server", streamable_http_path="/", stateless_http=True)
14521490

14531491

14541492
@mcp_at_root.tool()
@@ -1457,11 +1495,20 @@ def process_data(data: str) -> str:
14571495
return f"Processed: {data}"
14581496

14591497

1498+
# Create lifespan context manager to initialize the session manager
1499+
@contextlib.asynccontextmanager
1500+
async def lifespan(app: Starlette):
1501+
"""Context manager for managing MCP session manager lifecycle."""
1502+
async with mcp_at_root.session_manager.run():
1503+
yield
1504+
1505+
14601506
# Mount at /process - endpoints will be at /process instead of /process/mcp
14611507
app = Starlette(
14621508
routes=[
14631509
Mount("/process", app=mcp_at_root.streamable_http_app()),
1464-
]
1510+
],
1511+
lifespan=lifespan,
14651512
)
14661513
```
14671514

examples/snippets/servers/streamable_http_basic_mounting.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
66
"""
77

8+
import contextlib
9+
810
from starlette.applications import Starlette
911
from starlette.routing import Mount
1012

1113
from mcp.server.fastmcp import FastMCP
1214

1315
# Create MCP server
14-
mcp = FastMCP("My App")
16+
mcp = FastMCP("My App", stateless_http=True)
1517

1618

1719
@mcp.tool()
@@ -20,9 +22,18 @@ def hello() -> str:
2022
return "Hello from MCP!"
2123

2224

25+
# Create lifespan context manager to initialize the session manager
26+
@contextlib.asynccontextmanager
27+
async def lifespan(app: Starlette):
28+
"""Context manager for managing MCP session manager lifecycle."""
29+
async with mcp.session_manager.run():
30+
yield
31+
32+
2333
# Mount the StreamableHTTP server to the existing ASGI server
2434
app = Starlette(
2535
routes=[
2636
Mount("/", app=mcp.streamable_http_app()),
27-
]
37+
],
38+
lifespan=lifespan,
2839
)

examples/snippets/servers/streamable_http_host_mounting.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
uvicorn examples.snippets.servers.streamable_http_host_mounting:app --reload
66
"""
77

8+
import contextlib
9+
810
from starlette.applications import Starlette
911
from starlette.routing import Host
1012

1113
from mcp.server.fastmcp import FastMCP
1214

1315
# Create MCP server
14-
mcp = FastMCP("MCP Host App")
16+
mcp = FastMCP("MCP Host App", stateless_http=True)
1517

1618

1719
@mcp.tool()
@@ -20,9 +22,18 @@ def domain_info() -> str:
2022
return "This is served from mcp.acme.corp"
2123

2224

25+
# Create lifespan context manager to initialize the session manager
26+
@contextlib.asynccontextmanager
27+
async def lifespan(app: Starlette):
28+
"""Context manager for managing MCP session manager lifecycle."""
29+
async with mcp.session_manager.run():
30+
yield
31+
32+
2333
# Mount using Host-based routing
2434
app = Starlette(
2535
routes=[
2636
Host("mcp.acme.corp", app=mcp.streamable_http_app()),
27-
]
37+
],
38+
lifespan=lifespan,
2839
)

examples/snippets/servers/streamable_http_multiple_servers.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
uvicorn examples.snippets.servers.streamable_http_multiple_servers:app --reload
66
"""
77

8+
import contextlib
9+
810
from starlette.applications import Starlette
911
from starlette.routing import Mount
1012

1113
from mcp.server.fastmcp import FastMCP
1214

1315
# Create multiple MCP servers
14-
api_mcp = FastMCP("API Server")
15-
chat_mcp = FastMCP("Chat Server")
16+
api_mcp = FastMCP("API Server", stateless_http=True)
17+
chat_mcp = FastMCP("Chat Server", stateless_http=True)
1618

1719

1820
@api_mcp.tool()
@@ -32,10 +34,22 @@ def send_message(message: str) -> str:
3234
api_mcp.settings.streamable_http_path = "/"
3335
chat_mcp.settings.streamable_http_path = "/"
3436

37+
38+
# Create lifespan context manager to initialize both session managers
39+
@contextlib.asynccontextmanager
40+
async def lifespan(app: Starlette):
41+
"""Context manager for managing multiple MCP session managers."""
42+
async with contextlib.AsyncExitStack() as stack:
43+
await stack.enter_async_context(api_mcp.session_manager.run())
44+
await stack.enter_async_context(chat_mcp.session_manager.run())
45+
yield
46+
47+
3548
# Mount the servers
3649
app = Starlette(
3750
routes=[
3851
Mount("/api", app=api_mcp.streamable_http_app()),
3952
Mount("/chat", app=chat_mcp.streamable_http_app()),
40-
]
53+
],
54+
lifespan=lifespan,
4155
)

examples/snippets/servers/streamable_http_path_config.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
uvicorn examples.snippets.servers.streamable_http_path_config:app --reload
66
"""
77

8+
import contextlib
9+
810
from starlette.applications import Starlette
911
from starlette.routing import Mount
1012

1113
from mcp.server.fastmcp import FastMCP
1214

1315
# Configure streamable_http_path during initialization
1416
# This server will mount at the root of wherever it's mounted
15-
mcp_at_root = FastMCP("My Server", streamable_http_path="/")
17+
mcp_at_root = FastMCP("My Server", streamable_http_path="/", stateless_http=True)
1618

1719

1820
@mcp_at_root.tool()
@@ -21,9 +23,18 @@ def process_data(data: str) -> str:
2123
return f"Processed: {data}"
2224

2325

26+
# Create lifespan context manager to initialize the session manager
27+
@contextlib.asynccontextmanager
28+
async def lifespan(app: Starlette):
29+
"""Context manager for managing MCP session manager lifecycle."""
30+
async with mcp_at_root.session_manager.run():
31+
yield
32+
33+
2434
# Mount at /process - endpoints will be at /process instead of /process/mcp
2535
app = Starlette(
2636
routes=[
2737
Mount("/process", app=mcp_at_root.streamable_http_app()),
28-
]
38+
],
39+
lifespan=lifespan,
2940
)

0 commit comments

Comments
 (0)