Elixir wrapper for the Claude Code agent SDK.
Provides idiomatic Elixir access to claude_agent_session (Erlang/OTP
gen_statem) with lazy streaming via Stream.resource/3.
Quick Start
# Start a session
{:ok, session} = ClaudeEx.start_session(cli_path: "/usr/local/bin/claude")
# Blocking query — collects all messages
{:ok, messages} = ClaudeEx.query(session, "What is 2 + 2?")
# Streaming query — lazy enumerable
ClaudeEx.stream!(session, "Explain OTP supervision trees")
|> Enum.each(fn msg ->
case msg.type do
:assistant -> Enum.each(msg.content_blocks, &IO.inspect/1)
:result -> IO.puts(msg.content)
_ -> :ok
end
end)Session Options
:cli_path— Path to the Claude CLI executable (default:"claude"):work_dir— Working directory for the CLI subprocess:env— Environment variables as[{key, value}]charlists:buffer_max— Max raw binary buffer in bytes (default: 2MB):session_id— Resume a previous session (binary):model— Model to use (binary, e.g."claude-sonnet-4-6"):fallback_model— Fallback model used by Claude CLI when the primary model is unavailable:system_prompt— System prompt (binary or preset map):tools— Base built-in tool preset/list passed via--tools:max_turns— Maximum number of turns:resume— Resume a previous session (boolean):fork_session— Fork from an existing session (boolean):permission_mode— Permission mode (binary or atom):permission_prompt_tool_name— Tool name used by Claude for interactive permission prompts:permission_handler—fn(tool_name, tool_input, options) -> resultcallback:allowed_tools— List of allowed tool names:disallowed_tools— List of disallowed tool names:settings— Settings file path or JSON blob passed via--settings:add_dirs— Extra directories passed via repeated--add-dir:agents— Subagent configurations (map):mcp_servers— MCP server configurations (map):output_format— Structured output JSON schema (map):thinking— Thinking configuration (map):effort— Effort level (binary):max_budget_usd— Maximum cost budget (number):enable_file_checkpointing— Enable file checkpoints (boolean):plugins— Plugin configurations (list):hooks— Hook configurations (map):betas— Beta features to enable (list):sandbox— Sandbox configuration (map):debug— Enable debug mode (boolean):extra_args— Extra CLI arguments (map):client_app— Client application name (binary):user_input_handler—fn(request, context) -> {:ok, answer} | {:error, reason}callback for elicitation/user-input requests from the CLI:sdk_mcp_servers— In-process MCP servers (list of server maps frommcp_server/2):sdk_hooks— SDK lifecycle hooks (list of hook maps fromsdk_hook/2,3)
In-Process MCP Servers
Define custom tools as Elixir functions that Claude can call in-process:
tool = ClaudeEx.mcp_tool("greet", "Greet a user",
%{"type" => "object", "properties" => %{"name" => %{"type" => "string"}}},
fn input -> {:ok, [%{type: :text, text: "Hello, #{input["name"]}!"}]} end
)
server = ClaudeEx.mcp_server("my-tools", [tool])
{:ok, session} = ClaudeEx.start_session(sdk_mcp_servers: [server])Session History
Browse past Claude Code sessions without starting a new one:
{:ok, sessions} = ClaudeEx.list_sessions()
{:ok, messages} = ClaudeEx.get_session_messages("session-uuid")
Summary
Functions
Abort the current query. Alias for interrupt/1.
Get account information from the init response.
List active beta features from the system init data.
Get the API key source from the system init data.
Convert a single content_block into a flat message.
Supervisor child specification for embedding a session.
Get the CLI version from the system init data.
Run a command via universal command execution.
Get the current model from session info.
Get the current permission mode from session info.
Delete a session and its messages.
Extract all TodoWrite items from a list of messages.
Filter todo items by status.
Flatten an assistant message (with content_blocks) into individual messages.
Fork a tracked session into a new session ID.
Read all messages from a past Claude Code session transcript on disk.
Get session metadata by ID.
Get messages for a session.
Get messages with options.
Get the current health/state of a session.
Interrupt the current active query.
List available agents from the system init data.
List configured MCP servers from the system init data.
List past Claude Code session transcripts from disk.
List available plugins from the system init data.
List all tracked sessions.
List sessions with filters.
List available skills from the system init data.
List available tools from the system init data.
Create an MCP server with a list of tools.
Query MCP server health and status.
Create an MCP tool definition for in-process tool handling.
Convert a single flat message into a content_block.
Convert a list of flat messages into content_block() format.
Normalize a list of messages from any adapter into a uniform flat stream.
Get the output style from the system init data.
Send a query and collect all response messages (blocking).
Reconnect a failed MCP server by name.
Revert the visible session history to a prior boundary.
Revert file changes to a checkpoint identified by UUID.
Create an SDK lifecycle hook.
Create an SDK lifecycle hook with a matcher filter.
Send a raw control message to the session.
Check server health. Maps to session health + adapter info for Claude.
Query session capabilities and initialization data.
Set the maximum thinking tokens at runtime.
Dynamically add or replace MCP server configurations.
Change the model at runtime during a session.
Change the permission mode at runtime.
Create or replace share state for the current session.
Start a new Claude Code session.
Gracefully stop a session, closing the CLI subprocess.
Stop a running agent task by task ID.
Return a Stream that does not raise on errors.
Return a lazy Stream of messages for the given prompt.
Submit feedback via universal feedback tracking.
Generate and store a summary for the current session.
List available agents from the init response.
List available slash commands from the init response.
List available models from the init response.
Archive a thread.
Fork an existing thread.
List all threads for this session.
Read thread metadata, optionally including visible messages.
Resume an existing thread.
Rollback the visible thread history.
Start a new conversation thread.
Unarchive a thread.
Get a summary of todo counts by status.
Enable or disable an MCP server at runtime.
Respond to an agent request via universal turn response.
Clear any stored session revert state.
Revoke share state for the current session.
Get the working directory from the system init data.
Types
@type content_block() :: %{ :type => :text | :thinking | :tool_use | :tool_result | :raw, optional(:text) => binary(), optional(:thinking) => binary(), optional(:id) => binary(), optional(:name) => binary(), optional(:input) => map(), optional(:tool_use_id) => binary(), optional(:content) => binary(), optional(:raw) => map() }
@type hook_callback() :: (hook_context() -> :ok | {:deny, binary()})
@type hook_context() :: %{ event: atom(), agent_id: binary(), agent_transcript_path: binary(), agent_type: binary(), content: binary(), duration_ms: non_neg_integer(), interrupt: boolean(), params: map(), permission_prompt_tool_name: binary(), permission_suggestions: [any()], prompt: binary(), reason: term(), session_id: binary(), stop_hook_active: boolean(), stop_reason: atom() | binary(), system_info: map(), tool_input: map(), tool_name: binary(), tool_use_id: binary(), updated_permissions: map() }
@type hook_map() :: %{callback: hook_callback(), event: atom()}
@type mcp_server_map() :: %{ name: binary(), tools: [mcp_tool_map()], version: binary() }
@type message() :: %{ :type => :text | :assistant | :tool_use | :tool_result | :system | :result | :error | :user | :control_request | :control_response | :stream_event | :rate_limit_event | :tool_progress | :tool_use_summary | :thinking | :auth_status | :prompt_suggestion | :raw, optional(:content) => binary(), optional(:tool_name) => binary(), optional(:tool_input) => map(), optional(:raw) => map(), optional(:timestamp) => integer(), optional(:uuid) => binary(), optional(:session_id) => binary(), optional(:content_blocks) => [content_block()], optional(:parent_tool_use_id) => binary() | nil, optional(:message_id) => binary(), optional(:model) => binary(), optional(:error_info) => map(), optional(:system_info) => map(), optional(:duration_ms) => non_neg_integer(), optional(:duration_api_ms) => non_neg_integer(), optional(:num_turns) => non_neg_integer(), optional(:stop_reason) => binary(), optional(:stop_reason_atom) => stop_reason(), optional(:usage) => map(), optional(:model_usage) => map(), optional(:total_cost_usd) => number(), optional(:is_error) => boolean(), optional(:subtype) => binary(), optional(:errors) => [binary()], optional(:structured_output) => term(), optional(:permission_denials) => list(), optional(:fast_mode_state) => map(), optional(:is_replay) => boolean(), optional(:request_id) => binary(), optional(:request) => map(), optional(:response) => map(), optional(:rate_limit_status) => binary(), optional(:resets_at) => number(), optional(:rate_limit_type) => binary(), optional(:utilization) => number(), optional(:overage_status) => binary(), optional(:overage_resets_at) => number(), optional(:overage_disabled_reason) => binary(), optional(:is_using_overage) => boolean(), optional(:surpassed_threshold) => number() }
@type permission_mode() ::
:default | :accept_edits | :bypass_permissions | :plan | :dont_ask
@type query_opt() :: {:system_prompt, binary()} | {:tools, [binary()] | map()} | {:allowed_tools, [binary()]} | {:disallowed_tools, [binary()]} | {:max_tokens, pos_integer()} | {:max_turns, pos_integer()} | {:permission_mode, binary()} | {:model, binary()} | {:timeout, timeout()} | {:output_format, map()} | {:thinking, map()} | {:effort, binary()} | {:max_budget_usd, number()} | {:agent, binary()}
@type session() :: pid()
@type session_opt() :: {:cli_path, Path.t()} | {:work_dir, Path.t()} | {:env, [{charlist(), charlist()}]} | {:buffer_max, pos_integer()} | {:session_id, binary()} | {:model, binary()} | {:fallback_model, binary()} | {:system_prompt, binary() | map()} | {:tools, [binary()] | map()} | {:max_turns, pos_integer()} | {:resume, boolean()} | {:fork_session, boolean()} | {:continue, boolean()} | {:persist_session, boolean()} | {:permission_mode, binary() | permission_mode()} | {:permission_prompt_tool_name, binary()} | {:permission_handler, (binary(), map(), map() -> term())} | {:allowed_tools, [binary()]} | {:disallowed_tools, [binary()]} | {:settings, binary()} | {:add_dirs, [binary() | String.t() | Path.t()]} | {:agents, map()} | {:mcp_servers, map()} | {:output_format, map()} | {:thinking, map()} | {:effort, binary()} | {:max_budget_usd, number()} | {:enable_file_checkpointing, boolean()} | {:setting_sources, [binary()]} | {:plugins, [map()]} | {:hooks, map()} | {:betas, [binary()]} | {:include_partial_messages, boolean()} | {:prompt_suggestions, boolean()} | {:sandbox, map()} | {:debug, boolean()} | {:debug_file, binary()} | {:extra_args, map()} | {:client_app, binary()} | {:sdk_mcp_servers, [map()]} | {:sdk_hooks, [map()]}
@type stop_reason() ::
:end_turn
| :max_tokens
| :stop_sequence
| :refusal
| :tool_use_stop
| :unknown_stop
@type summary_info_map() :: %{ content: binary(), generated_at: integer(), generated_by: binary(), message_count: non_neg_integer(), session_id: binary() }
@type thread_info_map() :: %{ created_at: integer(), message_count: non_neg_integer(), session_id: binary(), status: :active | :archived | :completed | :paused, thread_id: binary(), updated_at: integer(), visible_message_count: non_neg_integer(), archived: boolean(), archived_at: integer(), metadata: map(), name: binary(), parent_thread_id: binary(), summary: map() }
@type thread_read_info_map() :: %{thread: thread_info_map(), messages: [map()]}
Functions
Abort the current query. Alias for interrupt/1.
Get account information from the init response.
Returns account details (email, org, subscription type, etc.) from the initialize control_response.
Examples
{:ok, account} = ClaudeEx.account_info(session)
account["email"] # => "user@example.com"
List active beta features from the system init data.
Get the API key source from the system init data.
@spec block_to_message(content_block()) :: %{ type: :raw | :text | :thinking | :tool_result | :tool_use, content: term(), raw: term(), tool_input: term(), tool_name: term(), tool_use_id: term() }
Convert a single content_block into a flat message.
@spec child_spec([session_opt()]) :: Supervisor.child_spec()
Supervisor child specification for embedding a session.
Examples
children = [
{ClaudeEx, cli_path: "claude", work_dir: "/my/project"}
]
Supervisor.start_link(children, strategy: :one_for_one)
Get the CLI version from the system init data.
@spec command_run(pid(), binary(), map()) :: {:ok, %{exit_code: integer(), output: binary()}} | {:error, {:port_exit, term()} | {:port_failed, term()} | {:timeout, timeout()}}
Run a command via universal command execution.
Get the current model from session info.
Extracts from the session's model field or system init data.
Get the current permission mode from session info.
@spec delete_session(binary()) :: :ok
Delete a session and its messages.
@spec extract_todos([message()]) :: [BeamAgent.Todo.todo_item()]
Extract all TodoWrite items from a list of messages.
Scans assistant messages for TodoWrite tool use blocks and returns
a flat list of todo items with :content, :status, and optional
:active_form fields.
@spec filter_todos([BeamAgent.Todo.todo_item()], BeamAgent.Todo.todo_status()) :: [ BeamAgent.Todo.todo_item() ]
Filter todo items by status.
Valid statuses: :pending, :in_progress, :completed.
Flatten an assistant message (with content_blocks) into individual messages.
Non-assistant messages pass through as a single-element list. Context fields from the parent are propagated to children.
@spec fork_session(pid(), map()) :: {:ok, session_info_map()} | {:error, :not_found}
Fork a tracked session into a new session ID.
@spec get_native_session_messages(binary(), [{:config_dir, binary()}]) :: {:ok, [map()]} | {:error, term()}
Read all messages from a past Claude Code session transcript on disk.
Parses the full JSONL file and returns messages in conversation order.
Options
:config_dir— Override the Claude config directory (default:~/.claude)
Examples
{:ok, messages} = ClaudeEx.get_native_session_messages("session-uuid-123")
@spec get_session(binary()) :: {:ok, session_info_map()} | {:error, :not_found}
Get session metadata by ID.
Get messages for a session.
Get messages with options.
@spec health(session()) :: :ready | :connecting | :initializing | :active_query | :error
Get the current health/state of a session.
Examples
:ready = ClaudeEx.health(session)
Interrupt the current active query.
Sends an interrupt signal to the CLI subprocess. The current query will terminate and the session returns to idle state.
List available agents from the system init data.
List configured MCP servers from the system init data.
@spec list_native_sessions(config_dir: binary(), cwd: binary(), limit: pos_integer()) :: {:ok, [ %{ file_path: binary(), modified_at: integer(), session_id: binary(), cwd: binary(), model: binary() } ]}
List past Claude Code session transcripts from disk.
Scans ~/.claude/projects/ for JSONL transcript files and returns
metadata (session_id, model, timestamps) sorted by most recent first.
Options
:config_dir— Override the Claude config directory (default:~/.claude):cwd— Filter to sessions for a specific working directory:limit— Maximum number of sessions to return
Examples
{:ok, sessions} = ClaudeEx.list_native_sessions()
{:ok, sessions} = ClaudeEx.list_native_sessions(limit: 10)
List available plugins from the system init data.
@spec list_sessions() :: {:ok, [session_info_map()]}
List all tracked sessions.
@spec list_sessions(map()) :: {:ok, [session_info_map()]}
List sessions with filters.
List available skills from the system init data.
List available tools from the system init data.
@spec mcp_server(binary(), [mcp_tool_map()]) :: mcp_server_map()
Create an MCP server with a list of tools.
The server is registered with the session at startup and handles JSON-RPC tool calls from Claude in-process.
Examples
tool = ClaudeEx.mcp_tool("greet", "Greet user", %{"type" => "object"},
fn _input -> {:ok, [%{type: :text, text: "Hello!"}]} end
)
server = ClaudeEx.mcp_server("my-tools", [tool])
{:ok, session} = ClaudeEx.start_session(sdk_mcp_servers: [server])
Query MCP server health and status.
Returns connection status, availability, and diagnostics for all configured MCP servers in the session.
Examples
{:ok, status} = ClaudeEx.mcp_server_status(session)
@spec mcp_tool(binary(), binary(), map(), (map() -> {:error, binary()} | {:ok, [map()]})) :: mcp_tool_map()
Create an MCP tool definition for in-process tool handling.
The handler function receives the tool input as a map and must return
{:ok, [content_result()]} or {:error, binary()}.
Examples
tool = ClaudeEx.mcp_tool("echo", "Echo input",
%{"type" => "object", "properties" => %{"text" => %{"type" => "string"}}},
fn input -> {:ok, [%{type: :text, text: input["text"]}]} end
)
@spec message_to_block(map()) :: content_block()
Convert a single flat message into a content_block.
@spec messages_to_blocks([map()]) :: [content_block()]
Convert a list of flat messages into content_block() format.
Supported types (text, thinking, tool_use, tool_result) map to their block equivalents. Other types are wrapped in raw blocks.
Normalize a list of messages from any adapter into a uniform flat stream.
Claude produces assistant messages with nested content_blocks.
All other adapters produce individual typed messages (text, tool_use, etc.).
This function flattens both into a uniform stream where each message has a
single, specific type — never nested content_blocks.
Context fields (uuid, session_id, model, timestamp) from assistant messages are propagated to flattened children.
Examples
# Works identically regardless of which adapter produced messages:
ClaudeEx.normalize_messages(messages)
|> Enum.filter(& &1.type == :text)
|> Enum.map(& &1.content)
|> Enum.join("")
Get the output style from the system init data.
Send a query and collect all response messages (blocking).
Returns the complete list of messages once the query finishes. This is the simple, synchronous interface.
Examples
{:ok, messages} = ClaudeEx.query(session, "Hello!")
last = List.last(messages)
IO.puts(last.content)
Reconnect a failed MCP server by name.
Examples
{:ok, _} = ClaudeEx.reconnect_mcp_server(session, "my_server")
@spec revert_session(pid(), map()) :: {:ok, session_info_map()} | {:error, :invalid_selector | :not_found}
Revert the visible session history to a prior boundary.
Revert file changes to a checkpoint identified by UUID.
Only meaningful when file checkpointing is enabled in session opts.
Examples
{:ok, _} = ClaudeEx.rewind_files(session, "msg-uuid-123")
@spec sdk_hook(atom(), hook_callback()) :: hook_map()
Create an SDK lifecycle hook.
Hooks fire at key session lifecycle points. Events:
:pre_tool_use— before tool execution (can deny):post_tool_use— after tool result received:stop— when result/completion received:session_start— when session enters ready state:session_end— when session terminates:user_prompt_submit— before query sent (can deny)
Examples
hook = ClaudeEx.sdk_hook(:pre_tool_use, fn ctx ->
case ctx.tool_name do
"Bash" -> {:deny, "No shell access"}
_ -> :ok
end
end)
{:ok, session} = ClaudeEx.start_session(sdk_hooks: [hook])
@spec sdk_hook(atom(), hook_callback(), %{tool_name: binary()}) :: hook_with_matcher_map()
Create an SDK lifecycle hook with a matcher filter.
The matcher's :tool_name (exact string or regex) restricts which
tools trigger the hook. Only relevant for tool-related events.
Examples
# Only fire on Bash tool
hook = ClaudeEx.sdk_hook(:pre_tool_use,
fn _ctx -> {:deny, "blocked"} end,
%{tool_name: "Bash"})
# Fire on Read* tools (regex)
hook = ClaudeEx.sdk_hook(:pre_tool_use,
fn _ctx -> :ok end,
%{tool_name: "Read.*"})
Send a raw control message to the session.
Low-level interface for sending arbitrary control protocol messages. Most users should prefer the higher-level convenience functions.
Examples
ClaudeEx.send_control(session, "setModel", %{"model" => "claude-sonnet-4-6"})
@spec server_health(pid()) :: {:ok, %{ adapter: :claude, health: :active_query | :connecting | :error | :initializing | :ready }}
Check server health. Maps to session health + adapter info for Claude.
Query session capabilities and initialization data.
Returns a map with:
:session_id— the session ID:system_info— parsed system init metadata (tools, model, etc.):init_response— raw initialize control_response
Available in all session states (connecting, initializing, ready, active_query, error).
Examples
{:ok, info} = ClaudeEx.session_info(session)
info.system_info.model # => "claude-sonnet-4-20250514"
info.system_info.tools # => ["Read", "Write", "Bash"]
@spec set_max_thinking_tokens(session(), pos_integer()) :: {:ok, term()} | {:error, term()}
Set the maximum thinking tokens at runtime.
Controls how many tokens the model can use for its internal reasoning/thinking process.
Examples
{:ok, _} = ClaudeEx.set_max_thinking_tokens(session, 8192)
Dynamically add or replace MCP server configurations.
Accepts a map of server name => server config. Existing servers with the same name are replaced; others are unaffected.
Examples
servers = %{"my_server" => %{"command" => "node", "args" => ["server.js"]}}
{:ok, _} = ClaudeEx.set_mcp_servers(session, servers)
Change the model at runtime during a session.
Examples
:ok = ClaudeEx.set_model(session, "claude-sonnet-4-20250514")
Change the permission mode at runtime.
Examples
:ok = ClaudeEx.set_permission_mode(session, "acceptEdits")
@spec start_session([session_opt()]) :: {:ok, session()} | {:error, term()}
Start a new Claude Code session.
Returns {:ok, pid} on success. The session process can be added
to your supervision tree via ClaudeEx.child_spec/1.
Examples
{:ok, session} = ClaudeEx.start_session(cli_path: "claude")
{:ok, session} = ClaudeEx.start_session(work_dir: "/my/project")
{:ok, session} = ClaudeEx.start_session(
cli_path: "claude",
model: "claude-sonnet-4-20250514",
permission_mode: :accept_edits
)
@spec stop(session()) :: :ok
Gracefully stop a session, closing the CLI subprocess.
Stop a running agent task by task ID.
Examples
{:ok, _} = ClaudeEx.stop_task(session, "task-abc")
@spec stream(session(), binary(), [query_opt()]) :: Enumerable.t()
Return a Stream that does not raise on errors.
Like stream!/3 but wraps messages in {:ok, msg} tuples and
returns {:error, reason} on failure instead of raising.
@spec stream!(session(), binary(), [query_opt()]) :: Enumerable.t()
Return a lazy Stream of messages for the given prompt.
Uses Stream.resource/3 to implement demand-driven consumption:
the gen_statem only parses the next JSONL line when the stream
consumer requests it. The query is dispatched to the CLI
immediately when stream!/3 is called; message consumption
is lazy/pull-based.
The stream halts automatically when a :result or :error message
is received, or when the query completes.
Examples
ClaudeEx.stream!(session, "Explain GenServer")
|> Stream.filter(& &1.type == :assistant)
|> Enum.flat_map(& &1.content_blocks)
|> Enum.filter(& &1.type == :text)
|> Enum.map(& &1.text)
|> Enum.join("")
# With options
ClaudeEx.stream!(session, "Hello", system_prompt: "Be brief")
|> Enum.to_list()
Submit feedback via universal feedback tracking.
@spec summarize_session(pid()) :: {:ok, summary_info_map()} | {:error, :not_found}
Generate and store a summary for the current session.
@spec summarize_session(pid(), map()) :: {:ok, summary_info_map()} | {:error, :not_found}
List available agents from the init response.
Examples
{:ok, agents} = ClaudeEx.supported_agents(session)
List available slash commands from the init response.
Returns the commands array from the initialize control_response, or an empty list if not yet initialized.
Examples
{:ok, commands} = ClaudeEx.supported_commands(session)
Enum.each(commands, &IO.inspect/1)
List available models from the init response.
Examples
{:ok, models} = ClaudeEx.supported_models(session)
@spec thread_archive(pid(), binary()) :: {:ok, thread_info_map()} | {:error, :not_found}
Archive a thread.
@spec thread_fork(pid(), binary()) :: {:ok, thread_info_map()} | {:error, :not_found}
Fork an existing thread.
@spec thread_fork(pid(), binary(), map()) :: {:ok, thread_info_map()} | {:error, :not_found}
@spec thread_list(pid()) :: {:ok, [thread_info_map()]}
List all threads for this session.
@spec thread_read(pid(), binary()) :: {:ok, thread_read_info_map()} | {:error, :not_found}
Read thread metadata, optionally including visible messages.
@spec thread_read(pid(), binary(), map()) :: {:ok, thread_read_info_map()} | {:error, :not_found}
@spec thread_resume(pid(), binary()) :: {:ok, thread_info_map()} | {:error, :not_found}
Resume an existing thread.
@spec thread_rollback(pid(), binary(), map()) :: {:ok, thread_info_map()} | {:error, :invalid_selector | :not_found}
Rollback the visible thread history.
@spec thread_start(pid(), map()) :: {:ok, %{ archived: false, created_at: integer(), message_count: 0, metadata: map(), name: binary(), session_id: binary(), status: :active, thread_id: binary(), updated_at: integer(), visible_message_count: 0, parent_thread_id: binary() }}
Start a new conversation thread.
@spec thread_unarchive(pid(), binary()) :: {:ok, thread_info_map()} | {:error, :not_found}
Unarchive a thread.
@spec todo_summary([BeamAgent.Todo.todo_item()]) :: %{ :total => non_neg_integer(), required(atom()) => non_neg_integer() }
Get a summary of todo counts by status.
Returns a map like %{pending: 2, in_progress: 1, completed: 3, total: 6}.
Enable or disable an MCP server at runtime.
Examples
{:ok, _} = ClaudeEx.toggle_mcp_server(session, "my_server", false)
{:ok, _} = ClaudeEx.toggle_mcp_server(session, "my_server", true)
Respond to an agent request via universal turn response.
@spec unrevert_session(pid()) :: {:ok, session_info_map()} | {:error, :not_found}
Clear any stored session revert state.
Get the working directory from the system init data.