sam@latino:~/projects/longhaul$ cat README.md
longhaul
Early-adopter MCP 2026-07-28 release-candidate server in Rust — stateless core, the Tasks extension, and statelessness proven by round-robining one client across two server instances.
lang Rust status active tests 67 repo github.com/slatino-dev/longhaul
- [MCP]
- [protocol implementation]
- [stateless servers]
- [axum]
- [conformance testing]
The MCP 2026-07-28 release candidate deletes the initialize handshake and assumes your server keeps no session — longhaul is a working answer to “fine: how?”, written while the spec is still moving.
This is an implementation log more than a product page. I built a server against the release candidate as it landed, partly to have one, mostly because reading a spec and implementing a spec are different levels of understanding and the second one is the only one that sticks.
log 01 — the handshake is gone
The RC removes initialize. There is no session to set up because there is no
session: server/discover answers capability questions statelessly, and
everything identity-shaped now rides on each request — clientInfo arrives
per-request in _meta, and three headers carry the rest.
POST /
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: indexer
{ "jsonrpc": "2.0", "method": "tools/call",
"params": { ..., "_meta": { "clientInfo": { ... } } } } The Mcp-Method header is a gift for routing: axum middleware dispatches on
it before the body is parsed, so the JSON-RPC layer stays thin. Tool schemas
are JSON Schema 2020-12. One error code moved — what was -32002 is now
-32602 — which is a small diff in the spec and a grep through every test
fixture in practice.
log 02 — long-running work becomes Tasks
The Tasks extension restructures tools/call: instead of blocking on slow
work, the call returns a task handle, and the client drives the lifecycle with
tasks/get, tasks/update, and tasks/cancel. tasks/list is gone from the
RC. A task can pause in InputRequired, ask the client a question, and resume
when the answer comes back via tasks/update — a real multi-round-trip, not a
callback hack.
The repo’s example server makes this concrete: an indexer whose
index_directory task walks a real directory tree with streamed progress,
honors cancellation mid-walk, and exercises one full InputRequired round
trip. A conformance runner drives the whole protocol surface from the outside.
log 03 — proving statelessness instead of asserting it
Every server claims to be stateless. The signature test makes the claim
falsifiable: spin up two longhaul instances that share nothing but a SQLite
file, then round-robin one client’s entire task lifecycle across them —
create the task on instance A, poll it on B, answer its InputRequired
question on A, cancel a sibling on B. If any state hides in process memory,
the lifecycle breaks and the test fails.
Storage sits behind a TaskStore trait — MemoryStore keeps unit tests fast,
SqliteStore carries the proof. 67 tests, clippy -D warnings clean.
- tests
- 67 incl. the round-robin proof
- task stores
- 2 memory · SQLite
- spec
- 2026-07-28 release candidate
- lint
- clippy -D zero warnings
log 04 — what conformance will say
The conformance runner exists and runs; published results don't, yet. What lands here: per-method conformance for this server against the RC surface, plus task-lifecycle timings across the memory and SQLite stores. Until those runs are written up, this table stays empty rather than approximate.