"""Server-side hosting scaffold for story examples. A story's ``server.py`` / ``server_lowlevel.py`` imports only from here. The marked lines touch entry-point APIs that a later release reshapes into free-function entries; isolating them here keeps story bodies stable. """ from __future__ import annotations import sys from collections.abc import Callable from typing import Any, TypeAlias import anyio import uvicorn from starlette.applications import Starlette from mcp.server.lowlevel import Server from mcp.server.mcpserver import MCPServer from mcp.server.stdio import stdio_server from mcp.server.transport_security import TransportSecuritySettings AnyServer: TypeAlias = "MCPServer | Server[Any]" ServerFactory = Callable[[], AnyServer] AppFactory = Callable[[], Starlette] NO_DNS_REBIND = TransportSecuritySettings(enable_dns_rebinding_protection=False) """Harness servers bind 127.0.0.1 and the in-process httpx client sends no Origin header.""" def argv_after(flag: str, *, default: str | None = None) -> str: """Return the argv token following ``flag``, or ``default`` when the flag is absent.""" try: return sys.argv[sys.argv.index(flag) + 1] except ValueError: if default is None: raise SystemExit(f"missing required {flag}") from None return default def asgi_from(server: AnyServer, *, path: str = "/mcp") -> Starlette: """Wrap a server instance in its streamable-HTTP ASGI app for in-process driving.""" return server.streamable_http_app( # becomes free fn streamable_http(server, legacy=...) streamable_http_path=path, stateless_http=False, # bool folds into a legacy= enum in a later release transport_security=NO_DNS_REBIND, ) def run_server_from_args(build_server: ServerFactory) -> None: """Entry point for ``if __name__ == "__main__"`` in every ``server*.py``. Bare argv serves over stdio; ``--http --port N [--path /mcp]`` serves over uvicorn on 127.0.0.1:N. """ server = build_server() if "--http" in sys.argv: port = int(argv_after("--port", default="8000")) path = argv_after("--path", default="/mcp") anyio.run(_serve_http, server, port, path) else: anyio.run(_serve_stdio, server) async def _serve_stdio(server: AnyServer) -> None: if isinstance(server, MCPServer): await server.run_stdio_async() # becomes await serve_stdio(server) else: async with stdio_server() as (read, write): # becomes await serve_stdio(server) await server.run(read, write, server.create_initialization_options()) async def _serve_http(server: AnyServer, port: int, path: str) -> None: app = asgi_from(server, path=path) config = uvicorn.Config(app, host="127.0.0.1", port=port, log_level="error") await uvicorn.Server(config).serve() def run_app_from_args(build_app: AppFactory) -> None: """Entry point for ``if __name__ == "__main__"`` in app-exporting ``server*.py``. App-exporting stories are HTTP-only; ``--port N`` serves the Starlette app over uvicorn on 127.0.0.1:N (uvicorn drives the app's own lifespan). No stdio leg. """ port = int(argv_after("--port", default="8000")) config = uvicorn.Config(build_app(), host="127.0.0.1", port=port, log_level="error") anyio.run(uvicorn.Server(config).serve)