# `Anubis.Server.Transport.Session`

Public contract between server transports and session processes.

Transports never talk to `Anubis.Server.Session` internals directly. Every
client message flows through a *session dispatcher* implementing this
behaviour, so the delivery mechanism can be swapped without touching
transport code (for example, to route sessions across a cluster).

## Wire protocol

The default dispatcher (`Anubis.Server.Transport.Session.Local`) delivers
messages to the session process with these shapes, which are part of the
public contract and handled by `Anubis.Server.Session`:

  * `{:mcp_request, message, context}` — synchronous `GenServer.call/3`.
    The session replies `{:ok, binary() | nil}` with the JSON-encoded
    response (`nil` when no response body is due), or `{:error, term()}`.
  * `{:mcp_notification, message, context}` — asynchronous `GenServer.cast/2`
    for client notifications.
  * `{:mcp_response, message, context}` — asynchronous `GenServer.cast/2`
    for client responses to server-initiated requests (sampling, roots,
    elicitation).

## Transport context

The `context` is a map carrying request metadata from the transport to the
session. Keys are transport-specific; the ones consumed by
`Anubis.Server.Session` are:

  * `:assigns` — map merged into the frame assigns
  * `:req_headers` — HTTP request headers, exposed on the context
  * `:remote_ip` — client IP, exposed on the context
  * `:auth` — validated auth claims, exposed on the context

Transports may add any other keys (e.g. `:type`, `:query_params`); sessions
ignore unknown keys.

## Swapping the dispatcher

All dispatch functions on this module delegate to the configured adapter,
which defaults to `Anubis.Server.Transport.Session.Local`:

    config :anubis_mcp, :session_dispatcher, MyApp.ClusterDispatcher

A custom dispatcher must implement this behaviour. It receives the same
`t:session/0` reference transports already resolved, so a cluster-aware
implementation can encode routing information in a `:via` tuple or resolve
the owning node from the session id carried in the `t:context/0`.

One dispatcher per application is enough: the decision it encodes (which
node owns a session) is a property of the deployment, not of an individual
server. Running two topologies in one application is the only case that
would need a per-server override, and it can be served by encoding the
destination in the session reference instead.

# `context`

```elixir
@type context() :: %{optional(atom()) =&gt; term()}
```

Transport context map. See the module documentation for known keys.

# `message`

```elixir
@type message() :: %{required(String.t()) =&gt; term()}
```

A decoded JSON-RPC message (map with string keys).

# `notification_wire`

```elixir
@type notification_wire() :: {:mcp_notification, message(), context()}
```

Wire shape for client notifications delivered to a session.

# `request_reply`

```elixir
@type request_reply() :: {:ok, binary() | nil} | {:error, term()}
```

Reply expected from a dispatched request.

# `request_wire`

```elixir
@type request_wire() :: {:mcp_request, message(), context()}
```

Wire shape for client requests delivered to a session.

# `response_wire`

```elixir
@type response_wire() :: {:mcp_response, message(), context()}
```

Wire shape for client responses delivered to a session.

# `session`

```elixir
@type session() :: GenServer.server()
```

A reference to a session process, as resolved by the transport.

# `dispatch_notification`

```elixir
@callback dispatch_notification(session(), message(), context()) :: :ok
```

Delivers a client notification to the session.

# `dispatch_request`

```elixir
@callback dispatch_request(session(), message(), context(), opts) :: request_reply()
when opts: [{:timeout, timeout()}]
```

Delivers a client request to the session and returns its reply.

Supported options:

  * `:timeout` — how long to wait for the session reply (default: 5000)

# `dispatch_response`

```elixir
@callback dispatch_response(session(), message(), context()) :: :ok
```

Delivers a client response (to a server-initiated request) to the session.

# `dispatch_notification`

```elixir
@spec dispatch_notification(session(), message(), context()) :: :ok
```

Dispatches a client notification through the configured dispatcher.

See `c:dispatch_notification/3`.

# `dispatch_request`

```elixir
@spec dispatch_request(session(), message(), context(), keyword()) :: request_reply()
```

Dispatches a client request through the configured dispatcher.

See `c:dispatch_request/4`.

# `dispatch_response`

```elixir
@spec dispatch_response(session(), message(), context()) :: :ok
```

Dispatches a client response through the configured dispatcher.

See `c:dispatch_response/3`.

---

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