Phantom.RequestState (phantom_mcp v0.7.3)

Copy Markdown View Source

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.
use Phantom.Router,
  name: "MyApp",
  secret_key_base: {Application, :fetch_env!, [:my_app, :mcp_secret_key_base]},
  request_state_salt: "myapp request_state v1"

Summary

Functions

Decrypts and verifies a requestState token.

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

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

Functions

decode(token, secret_key_base, salt, opts \\ [])

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(term, secret_key_base, salt)

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(term, binding, secret_key_base, salt)

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