Skip to content

Commit 2ea2b19

Browse files
committed
refactor(examples): migrate task/polling examples to streamable_http_app()
simple-task, simple-task-interactive, and sse-polling-demo demonstrate tasks and SSE polling — transport wiring is incidental. Migrate them to Server.streamable_http_app(), which handles session manager creation, lifespan, routing, and DNS rebinding auto-enable in one call. simple-streamablehttp and simple-streamablehttp-stateless stay on the low-level API since demonstrating manual StreamableHTTPSessionManager wiring is their purpose.
1 parent c5c4ee7 commit 2ea2b19

File tree

3 files changed

+7
-88
lines changed
  • examples/servers
    • simple-task-interactive/mcp_simple_task_interactive
    • simple-task/mcp_simple_task
    • sse-polling-demo/mcp_sse_polling_demo

3 files changed

+7
-88
lines changed

examples/servers/simple-task-interactive/mcp_simple_task_interactive/server.py

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,13 @@
66
- ServerTaskContext.elicit() and ServerTaskContext.create_message() queue requests properly
77
"""
88

9-
from collections.abc import AsyncIterator
10-
from contextlib import asynccontextmanager
119
from typing import Any
1210

1311
import click
1412
import uvicorn
1513
from mcp import types
1614
from mcp.server import Server, ServerRequestContext
1715
from mcp.server.experimental.task_context import ServerTaskContext
18-
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
19-
from mcp.server.transport_security import TransportSecuritySettings
20-
from starlette.applications import Starlette
21-
from starlette.routing import Mount
2216

2317

2418
async def handle_list_tools(
@@ -135,29 +129,10 @@ async def handle_call_tool(
135129
server.experimental.enable_tasks()
136130

137131

138-
def create_app(session_manager: StreamableHTTPSessionManager) -> Starlette:
139-
@asynccontextmanager
140-
async def app_lifespan(app: Starlette) -> AsyncIterator[None]:
141-
async with session_manager.run():
142-
yield
143-
144-
return Starlette(
145-
routes=[Mount("/mcp", app=session_manager.handle_request)],
146-
lifespan=app_lifespan,
147-
)
148-
149-
150132
@click.command()
151133
@click.option("--port", default=8000, help="Port to listen on")
152134
def main(port: int) -> int:
153-
session_manager = StreamableHTTPSessionManager(
154-
app=server,
155-
security_settings=TransportSecuritySettings(
156-
allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"],
157-
allowed_origins=["http://127.0.0.1:*", "http://localhost:*", "http://[::1]:*"],
158-
),
159-
)
160-
starlette_app = create_app(session_manager)
135+
starlette_app = server.streamable_http_app()
161136
print(f"Starting server on http://localhost:{port}/mcp")
162137
uvicorn.run(starlette_app, host="127.0.0.1", port=port)
163138
return 0

examples/servers/simple-task/mcp_simple_task/server.py

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
"""Simple task server demonstrating MCP tasks over streamable HTTP."""
22

3-
from collections.abc import AsyncIterator
4-
from contextlib import asynccontextmanager
5-
63
import anyio
74
import click
85
import uvicorn
96
from mcp import types
107
from mcp.server import Server, ServerRequestContext
118
from mcp.server.experimental.task_context import ServerTaskContext
12-
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
13-
from mcp.server.transport_security import TransportSecuritySettings
14-
from starlette.applications import Starlette
15-
from starlette.routing import Mount
169

1710

1811
async def handle_list_tools(
@@ -70,23 +63,7 @@ async def work(task: ServerTaskContext) -> types.CallToolResult:
7063
@click.command()
7164
@click.option("--port", default=8000, help="Port to listen on")
7265
def main(port: int) -> int:
73-
session_manager = StreamableHTTPSessionManager(
74-
app=server,
75-
security_settings=TransportSecuritySettings(
76-
allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"],
77-
allowed_origins=["http://127.0.0.1:*", "http://localhost:*", "http://[::1]:*"],
78-
),
79-
)
80-
81-
@asynccontextmanager
82-
async def app_lifespan(app: Starlette) -> AsyncIterator[None]:
83-
async with session_manager.run():
84-
yield
85-
86-
starlette_app = Starlette(
87-
routes=[Mount("/mcp", app=session_manager.handle_request)],
88-
lifespan=app_lifespan,
89-
)
66+
starlette_app = server.streamable_http_app()
9067

9168
print(f"Starting server on http://localhost:{port}/mcp")
9269
uvicorn.run(starlette_app, host="127.0.0.1", port=port)

examples/servers/sse-polling-demo/mcp_sse_polling_demo/server.py

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,13 @@
1212
uv run mcp-sse-polling-demo --port 3000
1313
"""
1414

15-
import contextlib
1615
import logging
17-
from collections.abc import AsyncIterator
1816

1917
import anyio
2018
import click
19+
import uvicorn
2120
from mcp import types
2221
from mcp.server import Server, ServerRequestContext
23-
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
24-
from mcp.server.transport_security import TransportSecuritySettings
25-
from starlette.applications import Starlette
26-
from starlette.routing import Mount
27-
from starlette.types import Receive, Scope, Send
2822

2923
from .event_store import InMemoryEventStore
3024

@@ -150,41 +144,14 @@ def main(port: int, log_level: str, retry_interval: int) -> int:
150144
on_call_tool=handle_call_tool,
151145
)
152146

153-
# Create event store for resumability
154-
event_store = InMemoryEventStore()
155-
156-
# Create session manager with event store and retry interval
157-
session_manager = StreamableHTTPSessionManager(
158-
app=app,
159-
event_store=event_store,
147+
starlette_app = app.streamable_http_app(
148+
event_store=InMemoryEventStore(),
160149
retry_interval=retry_interval,
161-
security_settings=TransportSecuritySettings(
162-
allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"],
163-
allowed_origins=["http://127.0.0.1:*", "http://localhost:*", "http://[::1]:*"],
164-
),
165-
)
166-
167-
async def handle_streamable_http(scope: Scope, receive: Receive, send: Send) -> None:
168-
await session_manager.handle_request(scope, receive, send)
169-
170-
@contextlib.asynccontextmanager
171-
async def lifespan(starlette_app: Starlette) -> AsyncIterator[None]:
172-
async with session_manager.run():
173-
logger.info(f"SSE Polling Demo server started on port {port}")
174-
logger.info("Try: POST /mcp with tools/call for 'process_batch'")
175-
yield
176-
logger.info("Server shutting down...")
177-
178-
starlette_app = Starlette(
179150
debug=True,
180-
routes=[
181-
Mount("/mcp", app=handle_streamable_http),
182-
],
183-
lifespan=lifespan,
184151
)
185152

186-
import uvicorn
187-
153+
logger.info(f"SSE Polling Demo server starting on port {port}")
154+
logger.info("Try: POST /mcp with tools/call for 'process_batch'")
188155
uvicorn.run(starlette_app, host="127.0.0.1", port=port)
189156
return 0
190157

0 commit comments

Comments
 (0)