Why must an MCP client echo requestState verbatim and never inspect it?
answer
- the server's private bookmark
- opaque like a pagination cursor
- format may change between deploys
- clients could otherwise forge a step
- any node must be able to resume
basics
~20 srequestState is the server's private continuation for a paused call. MCP 2026-07-28 defines it as opaque so the server may encode, sign or version it freely; clients that parse or rewrite it break the resume and couple themselves to one server's internals.
solid answer
~50 sWhen an MCP server returns an `InputRequiredResult`, it has already discarded the in-memory context of the paused call — the protocol is stateless and the retry may reach a different process entirely. `requestState` is how the server carries that context forward, and the specification makes it **opaque to the client**: the client copies the value into the retry byte for byte and MUST NOT inspect, decode or modify it. Opacity buys the server freedom — the blob may be a signed or encrypted envelope, a compressed continuation, or just a key into a short-lived store, and its format may change between deploys without breaking any client. It also protects integrity: a client that could rewrite it could resume a call at a step it never legitimately reached. Treat it exactly like an opaque cursor in a paginated API.
go deeper
Know the one rule that applies to you: copy requestState into the retry exactly as received, and never try to read or change it.
Explain that it is the server's continuation for a paused call, that opacity lets the server pick and change its encoding, and that each round supplies a fresh value to echo.
Bring the operational angle: sign or encrypt it, bound its size and lifetime, make it resumable on any node behind a load balancer, and reject tampered or expired values instead of repairing them.
Own the policy questions — whether continuation travels inline or behind a shared store, how long an abandoned flow may linger, what data may legally sit in the blob, and how retries are re-authorized rather than trusted on possession.
## What requestState is for MCP revision 2026-07-28 is explicitly a stateless protocol — every request is self-contained, and a server MUST NOT rely on prior requests over the same connection to establish context. That rule creates a problem for the Multi Round-Trip Request pattern: a server that pauses a `tools/call` to ask for input has, by design, no place to keep "where I was". `requestState` is the answer. It is a value the server puts in the `InputRequiredResult` and the client hands straight back on the retry, and it is the only thing linking the two rounds. ## Why the spec makes it opaque **Freedom of representation.** Because no client may look inside, a server can choose whatever encoding suits it: a serialized continuation, a compressed and base64-encoded struct, an encrypted envelope, a signed token, or simply a random key into a short-lived cache. It can change that choice in the next deploy and no client needs updating. Had the shape been specified, it would have become a public contract. **Integrity.** The blob encodes decisions the server already made — which tool, which arguments were validated, which step of a multi-round flow is next, sometimes an authorization context. If a client could edit it, it could resume at a step it never earned, replay a half-finished flow, or fabricate a state that skips a confirmation the server intended to require. Servers therefore commonly authenticate the blob (MAC or encryption) and validate it on receipt rather than trusting its contents; a tampered or expired value should be rejected with an invalid-params error, not silently repaired. **Portability across nodes.** A remote MCP server sits behind a load balancer, and since 2026-07-28 removed protocol sessions and the `Mcp-Session-Id` header, there is no affinity to pin the retry to the node that produced the interim result. A self-contained `requestState` means any node can resume. If the server instead stores continuation server-side and puts only a key in the blob, it has quietly reintroduced shared state — workable with a shared store, but a design choice to make consciously. ## The client's obligations 1. Copy it exactly. Do not parse it as JSON, do not re-encode it, do not trim it, do not normalize whitespace or case. 2. Do not persist assumptions about it. It is not a request id, not a session id, not a resumption token you may reuse for a different call. 3. Echo the **latest** one. Each round produces its own `requestState`; a multi-round flow must send the value from the most recent `InputRequiredResult`. 4. Do not log it carelessly. It may embed authorization context or user data, so treat it as sensitive material rather than a debug string. ## Server-side design guidance - **Bound its lifetime.** Put an expiry inside the blob (or on the referenced record) and reject stale ones. A client may retry hours later — or never. - **Bound its size.** It travels on every subsequent round. Large continuations belong behind a key, not inline. - **Do not treat possession as authentication.** MCP 2026-07-28 replaced the old "session hijacking" guidance with **State Handle Hijacking**: holding a handle MUST NOT be treated as proof of identity. The retry must be authorized on its own credentials, and the server should check that the caller is the same principal the blob was minted for. - **Assume abandonment.** A client that declines the requested input simply never retries, so nothing may be left holding a lock, an open transaction, or a reserved resource. ## Analogy in the wider API world The pattern is the same one behind opaque pagination cursors, OAuth `state`, and continuation tokens: the producer defines the meaning, the consumer promises only to give it back unchanged. Naming that family in an interview shows you understand *why* the field is typed as an opaque string rather than a structured object.
- What should a server do if the requestState it receives is invalid or expired?Reject the retry rather than guess. Validate integrity and expiry, then return an invalid-params error (-32602) explaining that the continuation is no longer usable, so the client can start the call fresh. Silently treating a bad blob as a new call risks re-running side effects the earlier round already performed.
- Does requestState identify the user, so can the server skip re-authorizing the retry?No. MCP 2026-07-28 states that possession of a state handle MUST NOT be treated as authentication — that is the State Handle Hijacking concern that replaced the old session-hijacking guidance. The retry is an ordinary request and must present valid credentials, which the server should also check match the principal the blob was issued for.
- Can the server keep the continuation in memory and put only an id in requestState?It can, but then any node that might serve the retry needs access to that store, since there is no session affinity after 2026-07-28. A shared cache with a TTL works; a per-process map does not once you run more than one replica behind a load balancer.
saying these in an interview costs you the question
- Parses requestState to show the user what is pending
- Assumes requestState is JSON with a stable shape
- Reuses one requestState across different calls
- Treats holding requestState as proof of identity
- Stores continuation in one process's memory behind a load balancer