# `ExMCP.Server.SSESession`
[🔗](https://github.com/azmaveth/ex_mcp/blob/v1.0.0/lib/ex_mcp/server/sse_session.ex#L1)

Server-side SSE session manager for bidirectional MCP communication.

Manages the lifecycle of SSE sessions where the server needs to send
requests to the client (elicitation, sampling) and receive responses.

## Architecture

Each MCP session has:
- A POST endpoint for client→server requests
- A GET SSE stream for server→client messages (notifications, requests)
- A pending request tracker for correlating server requests with client responses

## Usage

    # Initialize session state (call once at server startup)
    SSESession.init()

    # Register a GET SSE stream for a session
    SSESession.register_sse_stream(session_id)

    # Send a request to the client and wait for response
    {:ok, result} = SSESession.send_request(session_id, "elicitation/create", params)

    # Route a client response to the waiting request handler
    SSESession.handle_response(request_id, {:ok, result})

# `await_sole_live_session_id`

```elixir
@spec await_sole_live_session_id(non_neg_integer()) ::
  {:ok, String.t()} | {:error, :timeout | :ambiguous}
```

Wait until exactly one live SSE stream exists, then return its session id.

Handles the race where `tools/call` POST arrives slightly before the client's
GET SSE stream is registered (and the POST may omit `mcp-session-id`).

# `await_sse_stream`

```elixir
@spec await_sse_stream(String.t(), non_neg_integer()) :: :ok | {:error, :timeout}
```

Wait until a live SSE stream is registered for `session_id`.

Returns `:ok` when ready, or `{:error, :timeout}` if not registered within
`timeout_ms` (default 3000ms).

# `cleanup`

```elixir
@spec cleanup(String.t()) :: :ok
```

Clean up session state for an SSE stream registration.

# `handle_response`

```elixir
@spec handle_response(integer() | String.t(), {:ok, map()} | {:error, map()}) ::
  boolean()
```

Route a client response back to the waiting request handler.

Called when the server receives a POST with a JSON-RPC response
(has `id` + `result`/`error`, no `method`).

Accepts integer or string request ids (JSON number vs string echo).

# `has_sse_stream?`

```elixir
@spec has_sse_stream?(String.t()) :: boolean()
```

Check if a session has an active (live) SSE stream.

# `init`

```elixir
@spec init() :: :ok
```

Initialize the session ETS table.

# `register_sse_stream`

```elixir
@spec register_sse_stream(String.t()) :: :ok
```

Register the calling process as the SSE stream for a session.

Replaces any previous registration for the same session id (including dead pids).

# `run_sse_loop`

```elixir
@spec run_sse_loop(Plug.Conn.t(), String.t(), keyword()) :: Plug.Conn.t()
```

Run the SSE event loop for a GET connection.

Forwards `{:sse_send, data}` messages as SSE events.
Returns when `:sse_close` is received or timeout expires.
Always clears this process's registration for `session_id` on exit.

# `send_request`

```elixir
@spec send_request(String.t(), String.t(), map(), keyword()) ::
  {:ok, map()} | {:error, term()}
```

Send a JSON-RPC request to the client via the GET SSE stream.

Options:
- `:timeout` — wait for client response (default 10000ms)
- `:wait_for_stream` — wait for SSE registration before send (default 3000ms)

Blocks until the client responds or timeout is reached.
Returns `{:ok, result}` or `{:error, reason}`.

# `sole_live_session_id`

```elixir
@spec sole_live_session_id() :: {:ok, String.t()} | :error
```

If exactly one live SSE stream is registered, return its session id.

Used as a last-resort session resolution for bidirectional tools when the
client omits `mcp-session-id` on tools/call.

---

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