Skip to content

Configure your agent

Build the Agent SDK options object, choose a model and fallback, set environment and working directory, cap turns and spend, and change settings mid-session.

Every Agent SDK session takes its configuration from three places: settings files on disk, environment variables, and the options object you pass to query(). This page is about composing that options object well, plus the handful of general settings that do not belong to any single feature. For the full type of every field, see the TypeScript reference (Options) and the Python reference (ClaudeAgentOptions).

The options object

All fields are optional. A query() with no options runs on SDK defaults. Field names are camelCase in TypeScript and snake_case in Python.

Here is a read-only agent I use to produce a weekly summary of failing checks in a monorepo:

import { query } from "@anthropic-ai/claude-agent-sdk";

const run = query({
  prompt: "Read the last CI report in reports/ and list the failing packages with one-line causes",
  options: {
    model: "sonnet",
    cwd: "/srv/repos/platform",
    allowedTools: ["Read", "Glob", "Grep"],
    maxTurns: 10,
  },
});

for await (const msg of run) {
  if (msg.type === "result" && msg.subtype === "success") console.log(msg.result);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

async def main():
    opts = ClaudeAgentOptions(
        model="sonnet",
        cwd="/srv/repos/platform",
        allowed_tools=["Read", "Glob", "Grep"],
        max_turns=10,
    )
    async for msg in query(prompt="Read the last CI report in reports/ and list the failing packages", options=opts):
        if isinstance(msg, ResultMessage) and not msg.is_error:
            print(msg.result)

asyncio.run(main())

A common misunderstanding: allowedTools / allowed_tools is a pre-approval list, not a whitelist. Tools in the list run without asking. Tools outside it are still available, and the permission mode decides what happens when Claude calls one. To remove a tool entirely, use deny rules or the tools option; SDK permissions explains the difference.

Settings files

Two options decide which settings files contribute:

OptionWhat it does
settingSources / setting_sourcesChooses which filesystem sources load: "user", "project" and "local". Settings files and CLAUDE.md arrive through these. Omit it to load all three, pass [] to load none.
settingsSupplies settings directly: a file path or an inline JSON string in both languages, and also a plain object in TypeScript. Whatever you pass here beats user, project and local settings. Only managed policy settings outrank it.

Claude Code features in the SDK goes through what each source brings in.

Model and fallback

If nothing selects a model (no model option, no setting, no environment variable), the session uses Claude Code's default model. The model configuration page explains the precedence and lists the aliases.

model accepts an alias such as opus, sonnet or haiku, or a full model ID. Pin a full ID when you need reproducible behaviour; use a smaller model when speed and cost matter more than depth.

fallbackModel / fallback_model names what to use when the primary is overloaded or unavailable. The session switches over automatically and tries the primary again at the start of every user turn, so it drifts back once the outage clears. You can give one model or a comma-separated chain:

const options = {
  model: "opus",
  fallbackModel: "sonnet,haiku",
};
opts = ClaudeAgentOptions(model="opus", fallback_model="sonnet")

In TypeScript, setting the fallback to the same value as model throws at startup. The model configuration page covers how long a chain can be.

Note: There are no temperature, top_p or max_tokens fields. To trade depth for speed, use the effort level; to bound cost, use a budget cap. If you truly need raw sampling parameters, call the Messages API directly.

Environment variables

env sets environment variables for the Claude Code process behind the session. The two SDKs treat it differently, and this catches people out:

LanguageBehaviour of env
TypeScriptReplaces the child process's environment entirely
PythonMerges over the inherited environment; your values win

So in TypeScript, spread process.env in, or you lose PATH, HOME and ANTHROPIC_API_KEY:

const options = {
  env: { ...process.env, ANTHROPIC_BASE_URL: "https://llm-proxy.internal.example" },
};
opts = ClaudeAgentOptions(env={"ANTHROPIC_BASE_URL": "https://llm-proxy.internal.example"})

Leave env unset and the child inherits your environment in both languages. Anything Claude Code reads from the environment can be set this way; see the environment variables reference. The SDK references also describe the variables for tuning API timeouts and stall detection.

Working directory

cwd is where the session runs. Unset, it is your process's current directory. There is no way to change it on a live session; start a new session instead.

The working directory decides:

To let tools read and write outside it, add paths with additionalDirectories (TypeScript) or add_dirs (Python). These grant file access only; they do not load settings or CLAUDE.md from those folders. The permissions page has the details.

Turn and budget caps

Both caps are off unless you set them.

OptionEnds with result subtypeBehaviour of 0
maxTurns / max_turnserror_max_turnsMeans no limit, same as unset
maxBudgetUsd / max_budget_usderror_max_budget_usdRejected as invalid at startup; the session never runs

What happens when a cap trips depends on the input mode:

  • Single-message query(): you receive the cap result and then the SDK raises. Wrap the loop in try if you want to carry on afterwards.
  • Streaming input: the session survives. The turn count resets for each queued message, but spend accumulates across the whole conversation, so once the budget is used up every later message ends with the same budget result. A /clear resets the budget.

Subagent spend counts too. See turns and budget and cost tracking.

Changing configuration mid-session

With streaming input you can change the model and permission mode between turns.

MethodTypeScript (on the Query object)Python (on ClaudeSDKClient)
Switch modelsetModel(model?)set_model(model)
Switch permission modesetPermissionMode(mode)set_permission_mode(mode)

Python's plain query() returns a simple iterator with no control methods, which is why you need ClaudeSDKClient there. Calling the model setter with no model switches to Claude Code's default, not back to the model you started with.

TypeScript has two more:

  • applyFlagSettings(settings) applies settings at runtime, for example await run.applyFlagSettings({ effortLevel: "high" }). It takes settings-file keys, not options fields, and not every key takes effect mid-session.
  • updateSettings(source, settings) writes one allowlisted key to a settings file. With "localSettings" it writes the project's local settings (for example { outputStyle: "Explanatory" }), which applies from the next request and persists for future sessions that load local settings. With "userSettings" the only accepted key is effortLevel, saved as the default effort for the current model; it does not change the running session.

A Python example that escalates to a stronger model after a cheap first pass:

from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, ResultMessage

async def triage_then_fix():
    async with ClaudeSDKClient(options=ClaudeAgentOptions(model="haiku")) as client:
        await client.query("Read errors.log and tell me whether this is a config problem or a code bug. One word.")
        verdict = ""
        async for msg in client.receive_response():
            if isinstance(msg, ResultMessage):
                verdict = (msg.result or "").strip().lower()

        if "code" in verdict:
            await client.set_model("opus")
            await client.set_permission_mode("acceptEdits")
            await client.query("Find and fix the bug behind the errors in errors.log.")
            async for msg in client.receive_response():
                ...

In TypeScript, the pattern is to feed prompts from an async generator and hold back the next message until you have called the setters, so the second turn runs with the new settings.

Note: Prompt caches are per model. After switching, the next request re-reads the whole conversation uncached at the new model's price. The prompt caching page explains why.

Which option does what

TypeScriptPythonControlsRead more
permissionModepermission_modeHow much runs without approvalPermissions
allowedToolsallowed_toolsPre-approved toolsPermissions
canUseToolcan_use_toolYour approval callbackUser input
systemPromptsystem_promptInstructions to the agentSystem prompts
settingSourcessetting_sourcesFilesystem settings to loadClaude Code features
mcpServersmcp_serversExternal tool serversMCP
agentsagentsSubagent definitionsSubagents
hookshooksLifecycle callbacksHooks
skillsskillsWhich skills loadSkills
pluginspluginsPlugins to loadPlugins
outputFormatoutput_formatJSON schema for the resultStructured outputs
resumeresumeContinue a stored sessionSessions
forkSessionfork_sessionBranch a sessionSessions
sessionStoresession_storeExternal session persistenceSession storage
enableFileCheckpointingenable_file_checkpointingRewindable file editsFile checkpointing
efforteffortHow hard Claude thinksAgent loop
sandboxsandboxSandboxing of tool executionSecure deployment

If you know what you want but not which feature delivers it, Claude Code features in the SDK has a decision table. For multi-tenant services, hosting shows how settingSources, env and cwd combine to keep tenants apart.