# `Phantom.RequestState`
[🔗](https://github.com/dbernheisel/phantom_mcp/blob/main/lib/phantom/request_state.ex#L1)

Encode and decode the opaque `requestState` blob that powers multi-round-trip
tool calls under MCP `2026-07-28`.

Under the stateless core, a tool that needs additional client input returns
an `input_required` result containing a `requestState` value. The client
echoes that value back on the follow-up `tools/call`, and any server node
can pick up where the previous one left off — no sticky session, no
cross-node state replication.

The blob is authenticated-encrypted with `Plug.Crypto` (AES-256-GCM over an
HKDF-derived key, the same construction `Phoenix.Token` uses). Clients
cannot tamper with the contents, read them, or replay them past the
configured `max_age`.

## Configuration

Encoding requires two values, both set on the router:

- `:secret_key_base` — a high-entropy binary of at least 64 bytes (typically
  the same one your Phoenix endpoint uses), or a `{module, function, args}`
  tuple that returns one at runtime, such as
  `{MyAppWeb.Endpoint, :config, [:secret_key_base]}`.
- `:request_state_salt` — a string used as the HKDF salt to derive a key
  specifically for `requestState` blobs. Doesn't need to be secret, but
  must be stable; rotating it invalidates all in-flight blobs.

```elixir
use Phantom.Router,
  name: "MyApp",
  secret_key_base: {Application, :fetch_env!, [:my_app, :mcp_secret_key_base]},
  request_state_salt: "myapp request_state v1"
```

# `decode`

Decrypts and verifies a `requestState` token.

Returns `{:ok, term}` on success, `{:error, :expired}` if the token is
older than `max_age` seconds (default: 86400), or
`{:error, :invalid}` if the token has been tampered with, signed with a
different secret, or is otherwise unparseable.

# `encode`

Encrypts and signs `term` so it can travel through the client as a
`requestState` value.

- `secret_key_base` must be a binary of at least 64 bytes.
- `salt` is a stable string the router carries (see module doc).

# `encode`

Encrypts request state and binds it to the authenticated request context.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
