Files
modelcontextprotocol--pytho…/examples/stories/serve_one/README.md
T
wehub-resource-sync 49b9bb6724
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:10:27 +08:00

61 lines
2.7 KiB
Markdown

# 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
```bash
# 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](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle)
· [2026 versioning — discover](https://modelcontextprotocol.io/specification/draft/server/discover)
## See also
`legacy_routing/` (composing `serve_one` behind `classify_inbound_request`),
`dual_era/` (`Connection.protocol_version` in handlers).