# `Anubis.Server.Session`

Per-client MCP session process.

Each Session is a GenServer that manages the lifecycle of a single MCP client
connection. It handles protocol initialization, request/notification dispatch,
server-initiated requests (sampling, roots), and session persistence.

Sessions are created by the transport layer (STDIO creates one at startup,
HTTP transports create them dynamically via `Anubis.Server.Supervisor`).
Transports deliver client messages through the dispatcher contract defined
in `Anubis.Server.Transport.Session`.

# `t`

```elixir
@type t() :: %{
  session_id: String.t(),
  server_module: module(),
  protocol_version: String.t() | nil,
  protocol_module: module() | nil,
  initialized: boolean(),
  client_info: map() | nil,
  client_capabilities: map() | nil,
  init_meta: map(),
  log_level: String.t() | nil,
  frame: Anubis.Server.Frame.t(),
  server_info: map(),
  capabilities: map(),
  instructions: String.t() | nil,
  supported_versions: [String.t()],
  transport: %{layer: module(), name: GenServer.name()},
  registry: module(),
  session_idle_timeout: pos_integer(),
  expiry_timer: reference() | nil,
  pending_requests: %{
    required(String.t()) =&gt; %{started_at: integer(), method: String.t()}
  },
  server_requests: %{
    required(String.t()) =&gt; %{method: String.t(), timer_ref: reference()}
  },
  timeout: pos_integer(),
  task_supervisor: GenServer.name(),
  task_store: Anubis.Server.Session.Tasks.store() | nil,
  tasks: %{required(String.t()) =&gt; Anubis.Server.Session.Tasks.task_runtime()},
  task_refs: %{required(reference()) =&gt; String.t()},
  in_flight: Anubis.Server.Session.Scheduler.in_flight() | nil,
  request_queue: :queue.queue(Anubis.Server.Session.Scheduler.queued_request()),
  deferred_callbacks:
    :queue.queue(Anubis.Server.Session.Scheduler.deferred_callback()),
  owner_ref: reference() | nil,
  request_context: map() | nil
}
```

# `auto_initialize`

```elixir
@spec auto_initialize(GenServer.server()) :: :ok | {:error, term()}
```

Auto-initializes a session without a client initialize handshake.

This is used when a client sends a non-initialize request to an expired or
unknown session. Instead of returning 404, the server can create a new session
and auto-initialize it so the request can be processed transparently.

Uses the server's latest supported protocol version and synthetic client info
(`%{"name" => "auto-recovered", "version" => "unknown"}`). Server implementations
should not rely on this identity for client-specific decisions.

# `auto_initialize`

```elixir
@spec auto_initialize(GenServer.server(), map() | nil) :: :ok | {:error, term()}
```

# `get_schema`

# `parse_options`

# `parse_options!`

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a Session process linked to the current process.

## Options

  * `:session_id` — unique session identifier (required)
  * `:server_module` — the MCP server module implementing `Anubis.Server` (required)
  * `:name` — GenServer registration name. Omit it for a session that is only
    ever addressed by pid, such as one started for a single request.
  * `:transport` — transport configuration `[layer: module, name: name]` (required)
  * `:task_supervisor` — name of the `Task.Supervisor` for async work (required)
  * `:registry` — session registry module (default: `Anubis.Server.Registry`)
  * `:session_idle_timeout` — idle timeout in ms before session expires (default: 30 min)
  * `:timeout` — request timeout in ms (default: 30s)
  * `:owner` — a process whose exit stops the session. A transport that
    starts a session for a single stateless request passes itself, so the
    session never outlives the request, and it does not expire when idle.
    Such a session calls the server's `init/2` for each stateless request it
    serves, with that request's client info, because no handshake ever runs
    it.

---

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