Every attribute of ClaudeAgentSDK::ClaudeAgentOptions, with its type, its default, and what it turns into: a flag on the claude command line, a field of the control protocol's initialize request, a variable in the CLI's environment, or something the SDK handles on its own side. The topic guides explain how to use the larger features; this page is the complete list.
options = ClaudeAgentSDK::ClaudeAgentOptions.new(model: 'claude-sonnet-5', max_turns: 5)
ClaudeAgentSDK.query(prompt: 'Hello', options: options) { |message| puts message }- An unknown option name raises
ArgumentError, in.new, in#[]=and in#dup_with. Names may be Symbols or Strings, snake_case or camelCase. options.dup_with(max_turns: 1)returns a changed copy and leaves the original alone.ClaudeAgentSDK.configure { |config| config.default_options = { ... } }sets defaults for everyClaudeAgentOptionsbuilt afterwards. A value you pass replaces the configured one, with two exceptions:nilkeeps the configured value, and a Hash is merged into a configured Hash (that holds for every Hash-valued option,env,mcp_servers,settingsand a Hashsystem_promptoroutput_formatincluded).
- Type is what
.newaccepts for the option: the type of the constructor's keyword in the gem's RBS signatures (sig/claude_agent_sdk/types/options.rbs). Every option also acceptsnil.boolistrueorfalse. A name that starts with an underscore is an interface, so any object with the right method fits:callfor the callbacks,putsfor_Puts,appendandloadfor_SessionStore. - Default is what the option reads after a
ClaudeAgentOptions.newthat leaves it out, with no configured defaults. Leaving an option out, and passingnilhas the other cases. - Sent to the CLI as names the flag, the
initializefield (initialize.hooksis thehooksfield of that request) or the environment variable. "Nothing (SDK only)" marks an option that changes only what the SDK does in your process.
What an option reads after .new depends on what you pass for it, and on whether ClaudeAgentSDK.configure has set a default for it:
| You pass | No default is configured for the option | A default is configured for it |
|---|---|---|
| nothing | the Default column | the configured value |
nil |
the Default column, except where it says "explicit nil stays nil" |
the configured value |
| another value | that value | that value; a Hash is merged into a configured Hash |
The exception is every option whose default is false. With no default configured for it, ClaudeAgentOptions.new(fork_session: nil).fork_session is nil, not false. The option still acts as false: it adds nothing to what the SDK sends to the CLI, and its predicate (fork_session?) returns false. Only code that compares the reader's value with false sees the difference.
Three types in the tables are aliases:
| Alias | Stands for |
|---|---|
system_prompt_config |
String, SystemPromptPreset, SystemPromptCustom, SystemPromptFile, or the equivalent Hash ({ type: 'preset' | 'custom' | 'file', ... }) |
thinking_config |
ThinkingConfigAdaptive, ThinkingConfigEnabled, ThinkingConfigDisabled, or the equivalent Hash |
mcp_server_config |
McpStdioServerConfig, McpSSEServerConfig, McpHttpServerConfig, McpSdkServerConfig, or the equivalent Hash |
Guides: Structured Output, Thinking Configuration, Budget Control, Fallback Model, Advisor Model, Beta Features.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
system_prompt |
system_prompt_config |
nil |
--system-prompt, --system-prompt-file or --append-system-prompt, and the fields initialize.excludeDynamicSections and initialize.systemPromptSnapshot. See System prompt forms |
model |
String |
nil |
--model |
fallback_model |
String |
nil |
--fallback-model |
advisor_model |
String |
nil |
--advisor |
max_turns |
Integer |
nil |
--max-turns |
max_budget_usd |
Numeric |
nil |
--max-budget-usd |
task_budget |
TaskBudget | Hash[Symbol | String, untyped] |
nil |
--task-budget <total>. See task_budget |
thinking |
thinking_config |
nil |
--thinking adaptive, --thinking disabled or --max-thinking-tokens <budget_tokens>; display: adds --thinking-display |
effort |
String | Symbol | Integer |
nil |
--effort |
max_thinking_tokens |
Integer |
nil |
--max-thinking-tokens. Deprecated: used only when thinking is unset |
betas |
Array[String] |
nil |
--betas (comma-joined) |
output_format |
Hash[Symbol | String, untyped] | String |
nil |
--json-schema <schema as JSON> |
effort takes one of ClaudeAgentSDK::EFFORT_LEVELS; which levels a model supports is in Thinking Configuration.
system_prompt |
Command line |
|---|---|
nil (the default) |
--system-prompt "": an empty prompt, not Claude Code's default one |
a String, SystemPromptCustom, or { type: 'custom', prompt: '...' } |
--system-prompt <text> |
SystemPromptFile, or { type: 'file', path: '...' } (the path a String or a Pathname) |
--system-prompt-file <path> |
SystemPromptPreset, or { type: 'preset', preset: 'claude_code' } |
no system prompt flag, so the CLI uses its default prompt; append: adds --append-system-prompt <text> |
exclude_dynamic_sections on a preset is sent as initialize.excludeDynamicSections (Cross-User Prompt Caching), and snapshot on a preset or a custom prompt as initialize.systemPromptSnapshot (System Prompt Snapshot). Both are left out when unset.
A token budget for the task, counted on the API side: the model is told how much of it is left, so it can pace its tool use and wrap up before the limit. Pass ClaudeAgentSDK::TaskBudget.new(total: 50_000) or { total: 50_000 }. It is not max_budget_usd, which is a spending limit in dollars.
Guides: Tools Configuration, Skills, Sandbox Settings, Hooks & Permission Callbacks.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
tools |
Array[String] | ToolsPreset | Hash[Symbol | String, untyped] | String |
nil |
--tools <comma-joined names>; a String is sent as written, in the CLI's own comma-separated form. [] sends --tools "" (no built-in tools); a ToolsPreset, or { type: 'preset' }, sends --tools default |
allowed_tools |
Array[String] |
[] |
--allowedTools (comma-joined; no flag when empty) |
disallowed_tools |
Array[String] |
[] |
--disallowedTools (comma-joined; no flag when empty) |
permission_mode |
String |
nil |
--permission-mode |
can_use_tool |
_CanUseTool |
nil |
--permission-prompt-tool stdio: the CLI asks over the control protocol and the SDK calls the callback |
permission_prompt_tool_name |
String |
nil |
--permission-prompt-tool <name>. Combining it with can_use_tool raises ArgumentError |
hooks |
Hash[String | Symbol, Array[HookMatcher]?] |
nil |
initialize.hooks (matchers, callback ids, timeouts). The callbacks run in your process |
skills |
String | Array[String] |
nil |
Skill or Skill(name) entries in --allowedTools; an Array is also sent as initialize.skills; --setting-sources user,project when setting_sources is nil |
sandbox |
SandboxSettings | Hash[Symbol | String, untyped] | bool |
nil |
The sandbox key of --settings |
permission_mode takes one of ClaudeAgentSDK::PERMISSION_MODES.
Guides: Custom Tools (SDK MCP Servers), Subagent capabilities.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
mcp_servers |
Hash[String | Symbol, mcp_server_config] | String |
{} |
--mcp-config: a Hash as {"mcpServers": {...}} JSON, a String (a file path or JSON) as given. An SDK server contributes only its type and name; its tools are served in your process over the control protocol |
strict_mcp_config |
bool |
false; explicit nil stays nil |
--strict-mcp-config. See strict_mcp_config |
agents |
Hash[String | Symbol, AgentDefinition | Hash[Symbol | String, untyped]] |
nil |
initialize.agents |
plugins |
Array[SdkPluginConfig | Hash[Symbol | String, untyped]] |
nil |
--plugin-dir <path>, once per plugin. The path may be a String or a Pathname |
true makes the CLI use only the servers in mcp_servers and ignore every other MCP configuration (the CLI's own description of --strict-mcp-config: "Only use MCP servers from --mcp-config, ignoring all other MCP configurations").
"Every other" includes the MCP servers connected to the claude.ai account the CLI is logged in with. A session loads those by default, and setting_sources: [] does not keep them out, because they do not come from a settings file. client.mcp_status shows them: their :config has type: 'claudeai-proxy'. A host that runs sessions for other people under its own login should set strict_mcp_config: true.
Guides: Bare Mode, Verbatim Prompts, Session Isolation.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
settings |
String | Pathname | Hash[Symbol | String, untyped] |
nil |
--settings: a Hash as JSON, a String (a file path or JSON) as given, a Pathname as the path of a settings file. With sandbox set as well, one merged JSON value |
setting_sources |
Array[String] |
nil |
--setting-sources (comma-joined). [] sends --setting-sources ""; nil sends no flag |
add_dirs |
Array[String | Pathname] |
[] |
--add-dir, once per directory |
bare |
bool |
nil |
--bare |
verbatim_prompts |
bool |
false; explicit nil stays nil |
client_composed: true on every user message the SDK writes |
setting_sources takes entries of ClaudeAgentSDK::SETTING_SOURCES (user, project, local). With nil, the default, the CLI decides, and it loads the user's and the project's settings and CLAUDE.md files. [] loads none of the three sources. Neither value affects the auto-memory: see Session Isolation.
Guide: Session Browsing & Mutations, which also covers session_store. File Checkpointing & Rewind is in the configuration guide.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
resume |
String |
nil |
--resume=<session id> |
continue_conversation |
bool |
false; explicit nil stays nil |
--continue. Combining it with resume raises ArgumentError |
fork_session |
bool |
false; explicit nil stays nil |
--fork-session |
session_id |
String |
nil |
--session-id=<uuid> |
resume_session_at |
String |
nil |
--resume-session-at=<uuid>. Without resume it raises ArgumentError |
resume_drops_turn |
String |
nil |
--resume-drops-turn=<uuid> |
enable_file_checkpointing |
bool |
false; explicit nil stays nil |
CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true in the CLI's environment |
session_store |
_SessionStore |
nil |
--session-mirror. The SDK appends the mirrored transcript to the store and can resume from it |
session_store_flush |
String | Symbol |
"batched" |
Nothing (SDK only): 'batched' or 'eager' |
load_timeout_ms |
Numeric |
60000 |
Nothing (SDK only): the limit, in milliseconds, for each store call while a resume loads the transcript |
To find the session to resume, ClaudeAgentSDK.list_sessions lists sessions and ClaudeAgentSDK.get_session_info(session_id:, directory: nil, session_store: nil) reads the metadata of one session without listing the others. It returns an SDKSessionInfo, or nil when there is no session to report under that id. Both are plain functions that need neither a Client nor the CLI; the sessions guide has the rest of them.
Guides: Forwarding Subagent Text, Subagent Progress Summaries. The message classes are in the types reference.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
include_partial_messages |
bool |
false; explicit nil stays nil |
--include-partial-messages. See include_partial_messages |
include_hook_events |
bool |
false; explicit nil stays nil |
--include-hook-events. See include_hook_events |
forward_subagent_text |
bool |
false; explicit nil stays nil |
initialize.forwardSubagentText, sent only when true |
agent_progress_summaries |
bool |
nil |
initialize.agentProgressSummaries, left out when nil |
Asks the CLI for partial message chunks while the model is still producing a message. Each chunk arrives as a StreamEvent whose event is the raw API stream event, a Hash with Symbol keys; the complete AssistantMessage still follows. Without the option the CLI sends no StreamEvent.
Asks the CLI to put all hook lifecycle events into the message stream, as HookStartedMessage, HookProgressMessage and HookResponseMessage (see System and progress messages). It only adds messages: the callbacks you register with hooks are called whether or not it is set. It does not turn any off either: the CLI sends the events of SessionStart and Setup hooks whether or not it is set.
Guides: Vendoring the CLI, Custom Transport.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
cli_path |
String | Pathname |
nil |
The executable the SDK starts. nil: the discovery order |
cwd |
String | Pathname |
nil |
The working directory of the CLI process (and its PWD) |
env |
Hash[String | Symbol, String?] |
{} |
Merged over the environment the CLI process inherits. A nil value unsets the variable |
user |
String | Integer |
nil |
The user the CLI process runs as (name or uid; Unix, and the SDK process needs the privilege to switch) |
process_group |
bool |
false; explicit nil stays nil |
Nothing (SDK only): the CLI process starts in a process group of its own, out of reach of the terminal's Ctrl-C. See process_group |
extra_args |
Hash[String | Symbol, untyped] |
{} |
--<flag> <value> per pair, --<flag> for a nil value. See extra_args |
stderr |
_StderrCallback |
nil |
Nothing (SDK only): called with each line the CLI writes to stderr |
debug_stderr |
_Puts | String |
nil |
Nothing (SDK only): an IO-like object (puts) or a file path that receives each CLI stderr line |
max_buffer_size |
Integer |
nil |
Nothing (SDK only): the largest single message, in bytes, accepted from the CLI. nil means 1 MiB |
These options describe the process the SDK starts itself. A custom transport starts the CLI its own way and decides what to do with them.
In a terminal, Ctrl-C sends SIGINT to every process of the foreground process group. The CLI is in your process's group, so it gets the signal at the same moment your process does and ends the turn on its own, before your code can decide what to do: interrupt and keep the partial reply, ask whether to stop, or ignore the keypress. With process_group: true the CLI process the SDK starts for the session begins in a process group of its own, and the terminal's SIGINT no longer reaches it, nor the processes it starts that stay in its group (Bash tool commands, stdio MCP servers). The short claude -v version check that runs before it stays in your group.
It applies to query() and Client alike, on the default transport. A custom transport starts the CLI its own way. A transport subclass whose #build_command wraps the CLI in another program (docker exec, ssh) puts that local program in a group of its own; the option does not set the process group of a CLI on the far side. The Python SDK has no equivalent; the TypeScript SDK leaves it to a custom spawnClaudeCodeProcess.
Your process now owns Ctrl-C. To stop a turn, call Client#interrupt, or Client#disconnect to end the session; a one-shot query() has no handle to interrupt. Neither may be called inside a signal trap: they take a mutex, and Ruby raises ThreadError (can't be called from trap context). Let the trap only record the signal, and make the call from ordinary code:
interrupts = Thread::Queue.new
previous = trap('INT') { interrupts << :int } # nothing else in here: no SDK calls
begin
options = ClaudeAgentSDK::ClaudeAgentOptions.new(process_group: true)
ClaudeAgentSDK::Client.open(options: options) do |client|
watcher = Thread.new do
client.interrupt if interrupts.pop # on this thread, outside the trap
end
client.query('Refactor the parser')
client.receive_response { |message| puts message }
ensure
interrupts << nil # ends the watcher; the queue stays open for the trap
watcher&.join
end
ensure
trap('INT', previous) # Ctrl-C behaves as before once the session is over
endA plain script that does not trap INT is no worse off: Ctrl-C still raises Interrupt in your process, and the SDK's teardown stops the CLI as it does on any other exception.
When the CLI process stops:
- On
Client#disconnect, or at the end of aClient.openblock or aquery()call, the usual teardown runs: the SDK closes the CLI's stdin, gives it a grace period to finish, then sends SIGTERM and finally SIGKILL. - When the interpreter exits normally without a disconnect, an
at_exithandler sends SIGTERM to the CLI process only, not its group. Best effort: it does not wait for the turn, and sends no SIGKILL. - When your process dies abruptly (
exit!, SIGKILL), the SDK cleans up nothing. The CLI sees its stdin close, which normally lets it wind down; how soon is up to the CLI and its background work (seeCLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS).
SIGTERM still goes to the CLI process only, so the CLI can stop the processes it started in its own order (it does so for stdio MCP servers). Whenever the SDK sends the CLI SIGKILL, that SIGKILL also goes to the CLI's process group, so processes still in that group die with the CLI instead of outliving it: a CLI that has to be killed cannot stop them itself, and with the option on they no longer get the terminal's SIGINT either. The group SIGKILL follows the SDK's own SIGKILL decisions and adds no delay of its own. The SDK sends the CLI SIGKILL when it is still running:
- on a normal teardown (disconnect, the end of a
Client.openblock or aquery()call): after stdin EOF, a grace period, SIGTERM and a second grace period; - when that teardown is itself cancelled before it got there (
Async::Stop, a task timeout): SIGTERM at once, then SIGKILL after a short grace, with no grace for stdin EOF first; - when the CLI has closed its stdout but kept running: after a grace period of its own, SIGTERM and a second grace period.
What it does not do:
- When the CLI exits on its own, or on SIGTERM, before a SIGKILL is due, the SDK sends its group nothing. Its children are the CLI's to stop; one that ignores the CLI's request is not chased.
- A process that moved to another process group is not reached.
- The SDK does not wait for or reap the processes the group SIGKILL reaches: they are the CLI's children, not the SDK's.
- On Windows nothing changes: the SDK signals the CLI process only.
The CLI's group is a background group of the terminal. A process in it that reads from the controlling terminal (a tool command that prompts on the tty) is stopped with SIGTTIN, and so is the rest of the group, the CLI included when the tool shares its group. Whether writing to the terminal stops it too depends on the terminal's TOSTOP setting. The SDK's own traffic with the CLI goes through pipes and is unaffected.
On Windows the option starts the CLI with new_pgroup: true (CREATE_NEW_PROCESS_GROUP), which also turns Ctrl-C off for the CLI process. It has not been exercised on Windows.
The way to pass a CLI flag the SDK has no option for. A key is the flag's name without the leading dashes; the value follows it, and nil means a flag without a value:
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
extra_args: {
'no-session-persistence' => nil, # --no-session-persistence
'agent' => 'reviewer' # --agent reviewer
}
)- A flag name may contain only lowercase letters, digits and hyphens. Anything else raises
ArgumentErrorwhen the command line is built, before the CLI starts. - Values are converted with
to_s. One that starts with-is sent as--flag=value, so the CLI cannot read it as another flag. - The flags go at the end of the command line, after everything the options above produce. The SDK does not check that the CLI knows them: an unknown flag makes the CLI exit at startup, which raises
ProcessError(error: unknown option '--...'). - Use an option when there is one. A flag given here as well as through its option reaches the CLI twice.
Guides: Observability, Rails Integration.
| Option | Type | Default | Sent to the CLI as |
|---|---|---|---|
observers |
Array[untyped] |
[] |
Nothing (SDK only): observer instances, or callables that return a fresh one per query or session |
callback_scheduling |
:thread | :inline | "thread" | "inline" |
:thread |
Nothing (SDK only): :thread runs each callback on a plain thread, :inline on the reactor fiber. A String is stored as its Symbol (callback_scheduling: 'inline' reads back as :inline). A value that does not convert (through to_sym) to :thread or :inline raises ArgumentError; nil means the default |
callback_wrapper |
_CallbackWrapper |
nil |
Nothing (SDK only): a callable wrapped around every callback dispatch |
Variables that change what the SDK or a session does. "SDK process" means your Ruby process's own environment; env means ClaudeAgentOptions#env, which reaches only the CLI.
| Variable | Set it in | Effect |
|---|---|---|
ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN |
SDK process (the CLI inherits it) or env |
Credentials the CLI can authenticate with |
CLAUDE_CLI_PATH |
SDK process | The CLI executable, ahead of the vendored binary and PATH, when cli_path is nil. A relative path is resolved against the SDK process's working directory. A value that does not name an executable file is skipped without a warning, and discovery goes on to the vendored binary, PATH and the common install locations |
CLAUDE_CONFIG_DIR |
SDK process, and env for the CLI |
Where Claude Code keeps its configuration and transcripts, instead of ~/.claude. The session functions read it from the SDK process (Sessions) |
CLAUDE_AGENT_SDK_CONTROL_REQUEST_TIMEOUT_SECONDS |
SDK process | How long the SDK waits for the CLI's answer to a control request. Default 1200 (Error Handling) |
CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS |
env, or the SDK process |
See below |
CLAUDE_CODE_DISABLE_AUTO_MEMORY |
env |
'1' turns the CLI's auto-memory off (Session Isolation) |
CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS |
env |
'1' makes SessionStateChangedMessage reach your message block |
CLAUDE_CODE_DISABLE_ADVISOR_TOOL |
env |
'1' disables the advisor tool (Advisor Model) |
A one-shot query() or ask that has hooks, a can_use_tool callback or SDK MCP servers keeps the CLI's stdin open after a turn's result, because background work can still wake the session for another turn whose hook, permission and tool requests are answered over stdin. It closes stdin when the CLI reports that it is idle. This variable bounds that wait: if the CLI still reports work this many milliseconds after a result and no new turn has started, the run ends anyway.
- The default is 600000 (10 minutes).
0means no limit. - It is read from
envfirst and from the SDK process's environment otherwise. Only a plain non-negative integer counts; anything else gives the default. - The clock runs only between turns. A turn in progress, a request the SDK is still answering and a background agent that is still running stop it.
- The CLI reads the same variable for its own wait for background work once stdin is closed.
- A CLI that reports no session state (2.1.282 and earlier) is not waited for: stdin closes at the first result with no tracked background task still running.
- A
Clientsession driven withClient#queryis not affected: its stdin stays open untildisconnect.