# `Anubis.Server.Context`

Read-only session and request context, set by the SDK before each callback.

The Session process builds a fresh Context before every user callback invocation.
Mutations have no lasting effect — the Session always overwrites it.

For STDIO transport, `headers` is empty, `remote_ip` is nil, and `auth` is nil.
For HTTP transport, headers are normalized to lowercase string keys.

## Auth field

When OAuth 2.1 authorization is configured on the server, `auth` contains the
normalized claims map extracted from the validated bearer token:

    %{
      sub: "user-id",
      aud: "https://api.example.com",
      scope: "tools:read tools:write",
      scopes: ["tools:read", "tools:write"],
      exp: 1_234_567_890,
      iat: 1_234_567_800,
      client_id: "client-abc",
      raw_claims: %{}
    }

`auth` is `nil` when no authorization is configured or the transport is STDIO.

## Init meta field

`init_meta` carries the `_meta` map the client sent on its `initialize`
request params (the MCP extension namespace), available to every callback
including `init/2`. Empty map when the client sent none. Metadata sent under
`clientInfo._meta` is preserved inside `client_info` itself.

## Per-request fields

  * `session_pid` - the Session process serving this request. Use it to
    send notifications back to the client from any process, including a
    task spawned inside a tool callback. `nil` outside a session.
  * `request_meta` - the `_meta` map the client sent on this request's
    params. This is where `progressToken` and the
    `io.modelcontextprotocol/tasks` extension declaration live. Empty map
    when the client sent none.

## Protocol era fields

`protocol_version`, `protocol_module`, `client_capabilities` and `log_level`
describe the protocol the current request speaks.

Under the `:legacy` era they are settled once by the `initialize` handshake
and hold for the session. Under the `:stateless` era every request carries
its own, so they are rebuilt per request and never inherited — the
specification forbids a server from inferring capabilities from earlier
requests. `client_info` follows the same rule.

They are `nil` (and `client_capabilities` empty) before a legacy session has
been initialized.

## Multi round-trip fields

`input_responses` holds the answers a client sent to an
`Anubis.Server.InputRequired`, keyed as the input requests were, and
`request_state` the verified term a handler attached with
`Anubis.Server.InputRequired.state/2`. They are set for the `tools/call`,
`prompts/get` and `resources/read` request that carries them, and are empty
and `nil` otherwise. `request_digest` is a SHA-256 of that request's
parameters, less `_meta`, `inputResponses` and `requestState`: the signed
state is bound to it, so a retry must repeat the original request.

# `auth_claims`

```elixir
@type auth_claims() :: %{
  sub: String.t() | nil,
  aud: String.t() | [String.t()] | nil,
  scope: String.t() | nil,
  scopes: [String.t()],
  exp: integer() | nil,
  iat: integer() | nil,
  client_id: String.t() | nil,
  raw_claims: map()
}
```

# `t`

```elixir
@type t() :: %Anubis.Server.Context{
  auth: auth_claims() | nil,
  client_capabilities: map(),
  client_info: map() | nil,
  headers: %{required(String.t()) =&gt; String.t()},
  init_meta: map(),
  input_responses: %{required(String.t()) =&gt; map()},
  log_level: String.t() | nil,
  protocol_module: module() | nil,
  protocol_version: String.t() | nil,
  remote_ip: :inet.ip_address() | nil,
  request_digest: binary() | nil,
  request_meta: map(),
  request_state: term(),
  session_id: String.t() | nil,
  session_pid: pid() | nil
}
```

---

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