GenMCP.SessionController.Token (gen_mcp v2.0.0)

Copy Markdown View Source

The default GenMCP.SessionController: the session id is the session.

Nothing is stored server-side. The client data is sealed into the id itself with GenMCP.Token, using the application's secret_key_base, so GenMCP.SessionController.create/3 is an encrypt and GenMCP.SessionController.fetch/3 is a decrypt. There is no table, no process, and no coordination: a session minted on one node is readable on any other node sharing the configuration.

The trade-off is that a session cannot be revoked. It stops being accepted when it expires, and a client whose session expired mid-conversation gets a 404, the 2025 signal to handshake again.

Session lifetime

A session lasts one day. Expiry is the token's :max_age, and this controller sets its own default rather than taking the 20 minutes GenMCP.Token uses for pagination cursors, which is sized for a value replayed within a single listing rather than one a client holds across a whole conversation.

To choose another lifetime, pass :max_age in the controller's arg, which is handed through as the GenMCP.Token options:

forward "/mcp-2025", GenMCP.Transport.StreamableHTTP.V2511,
  server_name: "My App",
  server_version: "1.0.0",
  tools: [MyApp.AddTool],
  session_controller: {GenMCP.SessionController.Token, max_age: 3600}

The value is in seconds and is embedded in the token at mint time, so every session is read back under the lifetime it was issued with — changing this setting does not retroactively extend or shorten sessions already handed out.

Deployments that need revocation, or that need the full clientInfo and client capabilities server-side, implement their own controller instead.

To keep the Mcp-Session-Id header small the sealed payload is a pruned tuple, not the whole initialize params — clientInfo is unbounded, since it may carry descriptions and icon data URIs.