# `ExMCP.ACP.Client`
[🔗](https://github.com/azmaveth/ex_mcp/blob/v1.0.0/lib/ex_mcp/acp/client.ex#L1)

GenServer client for the Agent Client Protocol (ACP).

Manages connections to ACP-compatible coding agents over stdio, handling
the initialize handshake, session lifecycle, and bidirectional communication
(streaming updates from agent, permission/file requests from agent).

## Usage

    {:ok, client} = ExMCP.ACP.Client.start_link(
      command: ["gemini", "--acp"],
      handler: MyApp.ACPHandler
    )

    {:ok, %{"sessionId" => sid}} = ExMCP.ACP.Client.new_session(client, "/path/to/project")
    {:ok, %{"stopReason" => _}} = ExMCP.ACP.Client.prompt(client, sid, "Fix the bug in auth.ex")

## Options

- `:command` — command list for the agent subprocess (required)
- `:handler` — module implementing `ExMCP.ACP.Client.Handler` (default: `DefaultHandler`)
- `:handler_opts` — options passed to `handler.init/1` (default: `[]`)
- `:event_listener` — PID to receive `{:acp_session_update, session_id, update}` messages
- `:client_info` — `%{"name" => ..., "version" => ...}` (default: `%{"name" => "ex_mcp", "version" => "0.1.0"}`)
- `:capabilities` — client capabilities map
- `:protocol_version` — integer (default: 1)
- `:initialize_timeout` — total initialize-handshake timeout in milliseconds
  (default: 30_000)
- `:max_frame_bytes` — maximum inbound or outbound JSON-RPC frame size
  (default: 1 MiB)
- `:max_pending_requests` — maximum concurrent requests in either direction
  (default: 1,024)
- `:max_prompt_text_bytes` — maximum streamed prompt text retained per session
  (default: 1 MiB)
- `:pending_request_timeout` — server-side lifetime for outbound requests
  (default: 30_000 ms)
- `:handler_request_timeout` — lifetime for inbound client-handler callbacks
  (default: 30_000 ms)
- `:max_update_queue` — mailbox cutoff for handler/listener session updates;
  excess updates are dropped (default: 32)
- `:max_update_queue_bytes` — aggregate encoded size cutoff for queued
  handler/listener session updates (default: 8 MiB)
- `:name` — GenServer name registration

# `agent_capabilities`

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

Returns the agent's capabilities from the initialize handshake.

# `auth_methods`

```elixir
@spec auth_methods(GenServer.server()) :: {:ok, [map()]}
```

Returns the agent's authentication methods from the initialize handshake.

# `authenticate`

```elixir
@spec authenticate(GenServer.server(), String.t() | map(), keyword()) ::
  {:ok, map() | nil} | {:error, any()}
```

Authenticates with the agent.

Pass either a method ID advertised in the initialize response's
`"authMethods"` list or a full params map for adapter compatibility.

# `cancel`

```elixir
@spec cancel(GenServer.server(), String.t()) :: :ok
```

Cancels the current prompt in a session (fire-and-forget).

# `cancel_request`

```elixir
@spec cancel_request(GenServer.server(), integer() | String.t() | nil) :: :ok
```

Sends a `$/cancel_request` notification for a specific JSON-RPC request.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `close_session`

```elixir
@spec close_session(GenServer.server(), String.t(), keyword()) ::
  {:ok, map() | nil} | {:error, any()}
```

Closes an active session and frees agent-side resources.

# `delete_session`

```elixir
@spec delete_session(GenServer.server(), String.t(), keyword()) ::
  {:ok, map() | nil} | {:error, any()}
```

Deletes a session from the agent's session history.

# `disconnect`

```elixir
@spec disconnect(GenServer.server()) :: :ok
```

Disconnects from the agent.

# `end_session`

```elixir
@spec end_session(GenServer.server(), String.t()) ::
  :ok | {:ok, map() | nil} | {:error, any()}
```

Ends a session.

Uses `session/close` when advertised by the agent, otherwise preserves the
historical local telemetry-only behavior.

# `fork_session`

```elixir
@spec fork_session(GenServer.server(), String.t(), String.t(), keyword()) ::
  {:ok, map() | nil} | {:error, any()}
```

Forks an existing session into a new independent session.

`session/fork` is currently unstable in ACP and requires the agent to
advertise `sessionCapabilities.fork`.

# `list_sessions`

```elixir
@spec list_sessions(
  GenServer.server(),
  keyword()
) :: {:ok, map()} | {:error, any()}
```

Lists available sessions from the agent. Stabilized in ACP spec March 9, 2026.

# `load_session`

```elixir
@spec load_session(GenServer.server(), String.t(), String.t(), keyword()) ::
  {:ok, map()} | {:error, any()}
```

Loads an existing session and replays previous messages when the agent supports it.

`cwd` is required per ACP spec.

# `logout`

```elixir
@spec logout(
  GenServer.server(),
  keyword()
) :: {:ok, map() | nil} | {:error, any()}
```

Logs out of the current authenticated state if the agent supports `auth.logout`.

# `new_session`

```elixir
@spec new_session(GenServer.server(), String.t(), keyword()) ::
  {:ok, map()} | {:error, any()}
```

Creates a new agent session.

`cwd` is required per ACP spec
(https://agentclientprotocol.com/protocol/session-setup).

# `prompt`

```elixir
@spec prompt(GenServer.server(), String.t(), String.t() | [map()], keyword()) ::
  {:ok, map()} | {:error, any()}
```

Sends a prompt to the agent and blocks until the response arrives.

Streaming `session/update` notifications are delivered to the handler and
event listener as they arrive. The caller is unblocked when the agent sends
the JSON-RPC result for the prompt request.

# `resume_session`

```elixir
@spec resume_session(GenServer.server(), String.t(), String.t(), keyword()) ::
  {:ok, map() | nil} | {:error, any()}
```

Resumes an existing session without replaying previous messages.

`cwd` is required per ACP spec.

# `set_config_option`

```elixir
@spec set_config_option(GenServer.server(), String.t(), String.t(), any()) ::
  {:ok, map()} | {:error, any()}
```

Sets a config option for a session.

# `set_mode`

```elixir
@spec set_mode(GenServer.server(), String.t(), String.t()) ::
  {:ok, map()} | {:error, any()}
```

Sets the agent mode for a session.

# `set_model`

```elixir
@spec set_model(GenServer.server(), String.t(), String.t()) ::
  {:ok, map()} | {:error, any()}
```

Sets the model for a session.

# `start_link`

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

Starts the ACP client and connects to the agent.

# `status`

```elixir
@spec status(GenServer.server()) :: atom()
```

Returns the client connection status.

---

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