« All posts

MCP Sessions Moved Into the Payload: Sticky, Redis, Continuations Compared

MCP dropped server sessions for MRTR; a Rust rmcp v3 benchmark measures sticky, Redis, and sealed-continuation state strategies.

The Model Context Protocol's July 2026 revision removed server-held sessions (Mcp-Session-Id) and replaced server-initiated calls with MRTR, Multi Round-Trip Requests: when a tool call needs input, state is packed into an opaque requestState field and handed back to the client, which returns it on the next call. The spec deliberately stays silent on what goes inside that field — the choice moved from protocol to implementation.

A Rust project built on the three-day-old rmcp v3 SDK tested the three obvious answers behind one trait, on identical binaries and hardware: replica-affinity 'sticky' memory, a Redis-backed session store, and a self-contained, HMAC-sealed continuation carried entirely in the payload, with a gRPC held-stream as an out-of-band reference. Across 74,550 samples, wire-carried state proved essentially free at small sizes, but cost scaled with payload growth — roughly +120 microseconds per round by round ten as state grew from 250 to 923 bytes.

The Redis arm required nine round trips for a four-call dialogue, costing 4.84 ms per round at 1 ms store latency (1.40 ms on loopback), and failed outright when Redis was killed mid-dialogue. The sticky-memory arm leaked: all 100 abandoned dialogues stayed held in memory until process exit. Most notably, replaying an old requestState forked a single dialogue into two valid bookings priced differently — a reminder that MCP's safeguards are advisory (SHOULD), while the only strict (MUST) guarantee, at-most-once delivery, still depends on server-side state. The writeup draws a direct parallel to Rust's async fn, which compiles continuations into fixed-size state machines instead of keeping closures alive — the same architectural choice, now made explicit at the protocol level.