python-sdk/examples/stories/serve_one
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-06-26 12:02:27 +02:00
..
__init__.py Add story-style examples suite (27 stories + harness + CI) (#2957) 2026-06-26 12:02:27 +02:00
client.py Add story-style examples suite (27 stories + harness + CI) (#2957) 2026-06-26 12:02:27 +02:00
README.md Add story-style examples suite (27 stories + harness + CI) (#2957) 2026-06-26 12:02:27 +02:00
server.py Add story-style examples suite (27 stories + harness + CI) (#2957) 2026-06-26 12:02:27 +02:00

serve-one

The kernel layer beneath MCPServer.run() / run_server_from_args. Every transport entry composes the same three pieces: a lowlevel.Server (the handler registry), a Connection (per-peer state), and a driver — serve_one for one request → result dict, or serve_connection for a dispatcher loop. This is what you write to bring up MCP over a custom transport. Uniquely, the server file here builds the stdio entry by hand instead of importing stories._hosting.

Run it

# stdio (default — the client spawns server.py as a subprocess; its __main__
# is the hand-built serve_connection loop)
uv run python -m stories.serve_one.client

What to look at

  • server.py::handle_one — Connection.from_envelope(...) + serve_one(...) returns the raw result dict for one request. No handshake, no streams; the entry owns wire encoding and exception→error mapping.
  • server.py::main — JSONRPCDispatcher + Connection.for_loop(...) + serve_connection(...): exactly what Server.run() does internally for stdio.
  • server.py::SingleExchangeContext — the per-request DispatchContext a custom entry must supply. The SDK ships no public concrete class for this yet.
  • client.py — drives handle_one directly and asserts the raw result-dict shape (structuredContent / content), then proves the loop-mode driver works over the wire.

Caveats

  • Deep imports — serve_one, serve_connection, and Connection are only reachable at mcp.server.runner / mcp.server.connection today; a shorter mcp.server.* re-export is tracked for beta.
  • Lowlevel-only. The drivers take a lowlevel.Server and MCPServer has no public accessor for its underlying one (_lowlevel_server is private), so there is no MCPServer-tier variant of this story. Build the lowlevel Server directly until that accessor lands.
  • No public DispatchContext — SingleExchangeContext is hand-rolled boilerplate; a public helper (or a serve_one overload that builds one) is tracked for beta.
  • Lifespan — the transport entry enters server.lifespan(server) once and threads lifespan_state to every handle_one() call; never enter it per-request.
  • ServerRunner is kernel-internal; never construct it directly. The free-function drivers are the supported surface.

Spec

Architecture — lifecycle · 2026 versioning — discover

See also

legacy_routing/ (composing serve_one behind classify_inbound_request), dual_era/ (Connection.protocol_version in handlers).