chore: import upstream snapshot with attribution
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) Has been cancelled
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) Has been cancelled
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user