# `ClaudeAgentSDK.Client`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L1)

Bidirectional client for Claude Code with hooks support.

This GenServer maintains a persistent connection to the Claude CLI process,
handles control protocol messages, and invokes hook callbacks.

The Client enables:
- Bidirectional streaming communication
- Runtime hook callback invocation
- Control protocol request/response handling
- Message queueing and delivery

## Usage

    # Define hook callbacks
    def check_bash(input, _tool_use_id, _context) do
      if dangerous?(input), do: Output.deny("Blocked"), else: Output.allow()
    end

    # Configure options with hooks
    options = %Options{
      allowed_tools: ["Bash", "Write"],
      hooks: %{
        pre_tool_use: [
          Matcher.new("Bash", [&check_bash/3])
        ]
      }
    }

    # Start client
    {:ok, pid} = Client.start_link(options)

    # Send query
    Client.send_message(pid, "Run: echo 'Hello'")

    # Receive messages
    stream = Client.stream_messages(pid)
    Enum.each(stream, &IO.inspect/1)

    # Stop client
    Client.stop(pid)

## With Streaming

    {:ok, pid} = Client.start_link(options)

    # Start listening in separate process
    task = Task.async(fn ->
      Client.stream_messages(pid)
      |> Enum.take_while(&(&1.type != :result))
      |> Enum.to_list()
    end)

    # Send message
    Client.send_message(pid, "Write a function")

    # Wait for completion
    messages = Task.await(task, :infinity)

See: https://docs.anthropic.com/en/docs/claude-code/sdk

# `state`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L103)

```elixir
@type state() :: %{
  port: port() | nil,
  transport: pid() | nil,
  transport_module: module() | nil,
  transport_opts: keyword(),
  options: ClaudeAgentSDK.Options.t(),
  registry: ClaudeAgentSDK.Hooks.Registry.t(),
  hook_callback_timeouts: %{required(String.t()) =&gt; pos_integer()},
  subscribers: %{required(reference()) =&gt; pid()},
  pending_requests: %{required(String.t()) =&gt; {GenServer.from(), reference()}},
  pending_callbacks: %{
    required(String.t()) =&gt; %{
      pid: pid(),
      monitor_ref: reference(),
      signal: ClaudeAgentSDK.AbortSignal.t(),
      type: :hook | :permission
    }
  },
  initialized: boolean(),
  buffer: String.t(),
  sdk_mcp_servers: %{required(String.t()) =&gt; pid()},
  current_model: String.t() | nil,
  pending_model_change: {GenServer.from(), reference()} | nil,
  current_permission_mode: ClaudeAgentSDK.Permission.permission_mode() | nil,
  pending_permission_change: {GenServer.from(), reference()} | nil,
  permission_bridge: :ets.tid() | nil,
  accumulated_text: String.t(),
  active_subscriber: reference() | nil,
  subscriber_queue: [{reference(), String.t()}],
  init_waiters: [GenServer.from()],
  pending_inbound: :queue.queue(),
  pending_inbound_count: non_neg_integer(),
  pending_inbound_dropped: non_neg_integer(),
  stream_buffer_limit: non_neg_integer(),
  server_info: map() | nil,
  init_request_id: String.t() | nil,
  init_timeout_ref: reference() | nil,
  init_timeout_ms: pos_integer() | nil
}
```

Client state.

Fields:
- `port` - Port to Claude CLI process
- `options` - Configuration options
- `registry` - Hook callback registry
- `hook_callback_timeouts` - Map of callback_id => timeout_ms
- `subscribers` - Map of ref => pid for streaming subscriptions
- `pending_requests` - Map of request_id => {from, ref}
- `pending_callbacks` - Map of request_id => %{pid, monitor_ref, signal, type} for in-flight control callbacks
- `initialized` - Whether initialization handshake completed
- `buffer` - Incomplete JSON buffer
- `sdk_mcp_servers` - Map of server_name => registry_pid for SDK MCP servers
- `accumulated_text` - Buffer for partial text (streaming, v0.6.0)
- `active_subscriber` - Current streaming consumer reference (v0.6.0)
- `subscriber_queue` - Pending message queue (v0.6.0)
- `init_waiters` - Callers waiting for initialize request send
- `pending_inbound` - Buffered inbound events/messages before first subscriber
- `pending_inbound_count` - Count of buffered inbound entries
- `pending_inbound_dropped` - Dropped inbound entries due to buffer limit
- `stream_buffer_limit` - Max buffered inbound entries before first subscriber

# `await_init_sent`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L444)

```elixir
@spec await_init_sent(pid(), pos_integer() | nil) ::
  {:ok, String.t()} | {:error, term()}
```

Waits until the initialize request has been sent to the transport.

Returns `{:ok, request_id}` once the initialize request is sent, or
`{:error, reason}` if the client is not alive or times out.

# `await_initialized`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L425)

```elixir
@spec await_initialized(pid(), pos_integer() | nil) :: :ok | {:error, term()}
```

Waits for the client to finish initialization.

Returns `:ok` once the initialize handshake completes, or `{:error, reason}`
if the client is not alive or times out.

# `child_spec`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L64)

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `get_agent`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L563)

```elixir
@spec get_agent(pid()) :: {:ok, atom()} | {:error, term()}
```

Gets the currently active agent.

## Parameters

  * `client` - The client PID

## Returns

`{:ok, agent_name}` or `{:error, reason}`

## Examples

    {:ok, :coder} = Client.get_agent(client)

# `get_available_agents`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L583)

```elixir
@spec get_available_agents(pid()) :: {:ok, [atom()]} | {:error, term()}
```

Gets the list of available agent names.

## Parameters

  * `client` - The client PID

## Returns

`{:ok, [agent_name]}` or `{:error, reason}`

## Examples

    {:ok, [:coder, :researcher]} = Client.get_available_agents(client)

# `get_model`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L414)

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

Retrieves the currently active model name.

# `get_server_info`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L522)

```elixir
@spec get_server_info(pid()) :: {:ok, map()} | {:error, term()}
```

Returns the server initialization info provided by the CLI.

# `interrupt`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L396)

```elixir
@spec interrupt(pid()) :: :ok | {:error, term()}
```

Sends an interrupt control request to the CLI.

# `query`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L206)

```elixir
@spec query(pid(), String.t() | Enumerable.t(), String.t()) :: :ok | {:error, term()}
```

Sends a request in streaming mode, injecting `session_id` when missing.

Matches Python SDK behavior:
- String prompts are wrapped as a `"user"` message with `parent_tool_use_id: nil`
- Map prompts (or enumerables of maps) get `session_id` injected if absent

# `receive_response`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L277)

```elixir
@spec receive_response(pid()) ::
  {:ok, [ClaudeAgentSDK.Message.t()]} | {:error, term()}
```

Collects messages until a result frame is received.

Useful for workflows that only care about a single response and want
to avoid managing streaming state manually.

# `receive_response_stream`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L296)

```elixir
@spec receive_response_stream(pid()) :: Enumerable.t(ClaudeAgentSDK.Message.t())
```

Streams messages until a result frame is received.

This provides a streaming equivalent of `receive_response/1`.

# `rewind_files`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L406)

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

Rewinds tracked files to their state at a specific user message.

Requires `Options.enable_file_checkpointing` to be enabled when starting the client.

# `send_message`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L194)

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

Sends a message to Claude.

In streaming mode, this queues the message for sending.

## Parameters

- `client` - Client PID
- `message` - Message string or map

## Returns

`:ok` or `{:error, reason}`

## Examples

    Client.send_message(pid, "Write a hello world function")

# `set_agent`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L543)

```elixir
@spec set_agent(pid(), atom()) :: :ok | {:error, term()}
```

Switches to a different agent configuration.

## Parameters

  * `client` - The client PID
  * `agent_name` - The name of the agent to switch to (atom)

## Returns

`:ok` or `{:error, reason}`

## Examples

    Client.set_agent(client, :researcher)

# `set_model`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L388)

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

Requests a runtime model switch.

Returns `:ok` when the CLI confirms the change or `{:error, reason}`
when validation fails or the CLI rejects the request.

# `set_permission_mode`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L484)

```elixir
@spec set_permission_mode(pid(), ClaudeAgentSDK.Permission.permission_mode()) ::
  :ok | {:error, :invalid_permission_mode}
```

Sets the permission mode at runtime.

Changes how tool permissions are handled for subsequent tool uses.

## Parameters

- `client` - Client PID
- `mode` - Permission mode atom (`:default`, `:accept_edits`, `:plan`, `:bypass_permissions`, `:delegate`, `:dont_ask`)

## Returns

- `:ok` - Successfully changed mode
- `{:error, :invalid_permission_mode}` - Invalid mode provided

## Examples

    Client.set_permission_mode(pid, :plan)
    Client.set_permission_mode(pid, :accept_edits)
    Client.set_permission_mode(pid, :bypass_permissions)
    Client.set_permission_mode(pid, :delegate)

# `start_link`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L171)

```elixir
@spec start_link(
  ClaudeAgentSDK.Options.t(),
  keyword()
) :: GenServer.on_start()
```

Starts the client GenServer.

Validates hooks configuration, starts Claude CLI process, and performs
initialization handshake.

## Parameters

- `options` - ClaudeAgentSDK.Options struct with hooks configuration

## Returns

- `{:ok, pid}` - Successfully started
- `{:error, reason}` - Failed to start

## Examples

    options = %Options{
      hooks: %{
        pre_tool_use: [Matcher.new("Bash", [&my_hook/3])]
      }
    }

    {:ok, pid} = Client.start_link(options)

# `stop`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L375)

```elixir
@spec stop(pid()) :: :ok
```

Stops the client.

Terminates the CLI process and cleans up resources.

## Parameters

- `client` - Client PID

## Returns

`:ok`

## Examples

    Client.stop(pid)

# `stream_messages`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L256)

```elixir
@spec stream_messages(pid()) :: Enumerable.t(ClaudeAgentSDK.Message.t())
```

Returns a stream of messages from Claude.

Subscribes to the client and yields messages as they arrive.

## Parameters

- `client` - Client PID

## Returns

Enumerable stream of Message structs

## Examples

    Client.stream_messages(pid)
    |> Stream.filter(&(&1.type == :assistant))
    |> Enum.to_list()

# `subscribe`
[🔗](https://github.com/nshkrdotcom/claude_agent_sdk/blob/v0.9.2/lib/claude_agent_sdk/client.ex#L511)

```elixir
@spec subscribe(pid()) :: {pid(), reference() | nil}
```

Subscribes to the client's message stream and returns a subscription reference.

---

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