refactor: move transport-specific parameters from FastMCP constructor to run() (#1898)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Marcelo Trylesinski 2026-01-16 16:16:20 +01:00 • committed by GitHub
commit 8adb5bdce8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 339 additions and 231 deletions

View file

@ -8,7 +8,7 @@ Run from the repository root:
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo", json_response=True)
mcp = FastMCP("Demo")
# Add an addition tool
@ -40,4 +40,4 @@ def greet_user(name: str, style: str = "friendly") -> str:
# Run with streamable HTTP transport
if __name__ == "__main__":
mcp.run(transport="streamable-http")
mcp.run(transport="streamable-http", json_response=True)

View file

@ -20,7 +20,6 @@ class SimpleTokenVerifier(TokenVerifier):
# Create FastMCP instance as a Resource Server
mcp = FastMCP(
"Weather Service",
json_response=True,
# Token verifier for authentication
token_verifier=SimpleTokenVerifier(),
# Auth settings for RFC 9728 Protected Resource Metadata
@ -44,4 +43,4 @@ async def get_weather(city: str = "London") -> dict[str, str]:
if __name__ == "__main__":
mcp.run(transport="streamable-http")
mcp.run(transport="streamable-http", json_response=True)

View file

@ -5,15 +5,7 @@ Run from the repository root:
from mcp.server.fastmcp import FastMCP
# Stateless server with JSON responses (recommended)
mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
# Other configuration options:
# Stateless server with SSE streaming responses
# mcp = FastMCP("StatelessServer", stateless_http=True)
# Stateful server with session persistence
# mcp = FastMCP("StatefulServer")
mcp = FastMCP("StatelessServer")
# Add a simple tool to demonstrate the server
@ -24,5 +16,14 @@ def greet(name: str = "World") -> str:
# Run server with streamable_http transport
# Transport-specific options (stateless_http, json_response) are passed to run()
if __name__ == "__main__":
mcp.run(transport="streamable-http")
# Stateless server with JSON responses (recommended)
mcp.run(transport="streamable-http", stateless_http=True, json_response=True)
# Other configuration options:
# Stateless server with SSE streaming responses
# mcp.run(transport="streamable-http", stateless_http=True)
# Stateful server with session persistence
# mcp.run(transport="streamable-http")

View file

@ -13,7 +13,7 @@ from starlette.routing import Mount
from mcp.server.fastmcp import FastMCP
# Create MCP server
mcp = FastMCP("My App", json_response=True)
mcp = FastMCP("My App")
@mcp.tool()
@ -30,9 +30,10 @@ async def lifespan(app: Starlette):
# Mount the StreamableHTTP server to the existing ASGI server
# Transport-specific options are passed to streamable_http_app()
app = Starlette(
routes=[
Mount("/", app=mcp.streamable_http_app()),
Mount("/", app=mcp.streamable_http_app(json_response=True)),
],
lifespan=lifespan,
)

View file

@ -13,7 +13,7 @@ from starlette.routing import Host
from mcp.server.fastmcp import FastMCP
# Create MCP server
mcp = FastMCP("MCP Host App", json_response=True)
mcp = FastMCP("MCP Host App")
@mcp.tool()
@ -30,9 +30,10 @@ async def lifespan(app: Starlette):
# Mount using Host-based routing
# Transport-specific options are passed to streamable_http_app()
app = Starlette(
routes=[
Host("mcp.acme.corp", app=mcp.streamable_http_app()),
Host("mcp.acme.corp", app=mcp.streamable_http_app(json_response=True)),
],
lifespan=lifespan,
)

View file

@ -13,8 +13,8 @@ from starlette.routing import Mount
from mcp.server.fastmcp import FastMCP
# Create multiple MCP servers
api_mcp = FastMCP("API Server", json_response=True)
chat_mcp = FastMCP("Chat Server", json_response=True)
api_mcp = FastMCP("API Server")
chat_mcp = FastMCP("Chat Server")
@api_mcp.tool()
@ -29,12 +29,6 @@ def send_message(message: str) -> str:
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 = "/"
# Create a combined lifespan to manage both session managers
@contextlib.asynccontextmanager
async def lifespan(app: Starlette):
@ -44,11 +38,12 @@ async def lifespan(app: Starlette):
yield
# Mount the servers
# Mount the servers with transport-specific options passed to streamable_http_app()
# streamable_http_path="/" means endpoints will be at /api and /chat instead of /api/mcp and /chat/mcp
app = Starlette(
routes=[
Mount("/api", app=api_mcp.streamable_http_app()),
Mount("/chat", app=chat_mcp.streamable_http_app()),
Mount("/api", app=api_mcp.streamable_http_app(json_response=True, streamable_http_path="/")),
Mount("/chat", app=chat_mcp.streamable_http_app(json_response=True, streamable_http_path="/")),
],
lifespan=lifespan,
)

View file

@ -1,5 +1,5 @@
"""
Example showing path configuration during FastMCP initialization.
Example showing path configuration when mounting FastMCP.
Run from the repository root:
uvicorn examples.snippets.servers.streamable_http_path_config:app --reload
@ -10,13 +10,8 @@ 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",
json_response=True,
streamable_http_path="/",
)
# Create a simple FastMCP server
mcp_at_root = FastMCP("My Server")
@mcp_at_root.tool()
@ -25,9 +20,13 @@ def process_data(data: str) -> str:
return f"Processed: {data}"
# Mount at /process - endpoints will be at /process instead of /process/mcp
# Mount at /process with streamable_http_path="/" so the endpoint is /process (not /process/mcp)
# Transport-specific options like json_response are passed to streamable_http_app()
app = Starlette(
routes=[
Mount("/process", app=mcp_at_root.streamable_http_app()),
Mount(
"/process",
app=mcp_at_root.streamable_http_app(json_response=True, streamable_http_path="/"),
),
]
)

View file

@ -11,7 +11,7 @@ from starlette.routing import Mount
from mcp.server.fastmcp import FastMCP
# Create the Echo server
echo_mcp = FastMCP(name="EchoServer", stateless_http=True, json_response=True)
echo_mcp = FastMCP(name="EchoServer")
@echo_mcp.tool()
@ -21,7 +21,7 @@ def echo(message: str) -> str:
# Create the Math server
math_mcp = FastMCP(name="MathServer", stateless_http=True, json_response=True)
math_mcp = FastMCP(name="MathServer")
@math_mcp.tool()
@ -42,13 +42,13 @@ async def lifespan(app: Starlette):
# Create the Starlette app and mount the MCP servers
app = Starlette(
routes=[
Mount("/echo", echo_mcp.streamable_http_app()),
Mount("/math", math_mcp.streamable_http_app()),
Mount("/echo", echo_mcp.streamable_http_app(stateless_http=True, json_response=True)),
Mount("/math", math_mcp.streamable_http_app(stateless_http=True, json_response=True)),
],
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 = "/"
# echo_mcp.streamable_http_app(streamable_http_path="/", stateless_http=True, json_response=True)
# math_mcp.streamable_http_app(streamable_http_path="/", stateless_http=True, json_response=True)