Hermes.Server.Session (hermes_mcp v0.10.0)

Manages state for the Hermes MCP server base implementation.

This module provides a structured representation of server state during the MCP lifecycle, including initialization status, protocol negotiation, and server capabilities.

State Structure

Each server state includes:

  • protocol_version: Negotiated MCP protocol version
  • frame: Server frame (similar to LiveView socket)
  • initialized: Whether the server has completed initialization
  • client_info: Client information received during initialization
  • client_capabilities: Client capabilities received during initialization

Summary

Functions

Returns a specification to start this module under a supervisor.

Removes a completed request from tracking.

Retrieves the current state of a session.

Gets all pending requests for a session.

Checks if a request is currently pending.

Guard to check if a session has been initialized.

Marks the session as initialized.

Creates a new server state with the given options.

Updates the log level.

Starts a new session agent with initial state.

Tracks a new pending request in the session.

Updates state after successful initialization handshake.

Types

t()

@type t() :: %Hermes.Server.Session{
  client_capabilities: map() | nil,
  client_info: map() | nil,
  id: String.t() | nil,
  initialized: boolean(),
  log_level: String.t(),
  name: GenServer.name() | nil,
  pending_requests: %{
    required(String.t()) => %{started_at: integer(), method: String.t()}
  },
  protocol_version: String.t() | nil
}

Functions

child_spec(arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

complete_request(session, request_id)

@spec complete_request(GenServer.name(), String.t()) :: map() | nil

Removes a completed request from tracking.

Parameters

  • session - The session agent name or PID
  • request_id - The request ID to remove

Returns

The request info if found, nil otherwise.

Examples

iex> Session.complete_request(session, "req_123")
%{started_at: 1234567890, method: "tools/call"}

get(session)

@spec get(GenServer.name()) :: t()

Retrieves the current state of a session.

Parameters

  • session - The session agent name or PID

Returns

The current session state as a %Session{} struct.

Examples

iex> session_state = Session.get(session_name)
%Session{initialized: true, protocol_version: "2025-03-26"}

get_pending_requests(session)

@spec get_pending_requests(GenServer.name()) :: map()

Gets all pending requests for a session.

Parameters

  • session - The session agent name or PID

Examples

iex> Session.get_pending_requests(session)
%{"req_123" => %{started_at: 1234567890, method: "tools/call"}}

has_pending_request?(session, request_id)

@spec has_pending_request?(GenServer.name(), String.t()) :: boolean()

Checks if a request is currently pending.

Parameters

  • session - The session agent name or PID
  • request_id - The request ID to check

Examples

iex> Session.has_pending_request?(session, "req_123")
true

is_initialized(session)

(macro)

Guard to check if a session has been initialized.

Examples

iex> session = %Session{initialized: true}
iex> is_initialized(session)
true

mark_initialized(session)

@spec mark_initialized(GenServer.name()) :: :ok

Marks the session as initialized.

new(opts)

@spec new(Enumerable.t()) :: t()

Creates a new server state with the given options.

Parameters

  • opts - Map containing the initialization options

set_log_level(session, level)

@spec set_log_level(GenServer.name(), String.t()) :: :ok

Updates the log level.

start_link(opts \\ [])

@spec start_link(keyword()) :: Agent.on_start()

Starts a new session agent with initial state.

track_request(session, request_id, method)

@spec track_request(GenServer.name(), String.t(), String.t()) :: :ok

Tracks a new pending request in the session.

Parameters

  • session - The session agent name or PID
  • request_id - The unique request ID
  • method - The MCP method being called

Examples

iex> Session.track_request(session, "req_123", "tools/call")
:ok

update_from_initialization(session, negotiated_version, client_info, capabilities)

@spec update_from_initialization(GenServer.name(), String.t(), map(), map()) :: :ok

Updates state after successful initialization handshake.

This function:

  1. Sets the negotiated protocol version
  2. Stores client information and capabilities
  3. Marks the server as initialized

Parameters

  • state - The current server state
  • protocol_version - The negotiated protocol version
  • client_info - Client information from the initialize request
  • client_capabilities - Client capabilities from the initialize request

Examples

iex> client_info = %{"name" => "hello", "version" => "1.0.0"}
iex> capabilities = %{"sampling" => %{}}
iex> Hermes.Server.Session.update_from_initialization(session, "2025-03-26", client_info, capabilities)
:ok