The Agent SDK: Multi-Turn Assistants, Memory, and Sub-Agents
Learning Objectives
Assemble a multi-turn assistant with the Claude Agent SDK instead of hand-rolling the agent loop.
Configure and manage memory and context so long-running sessions stay coherent and affordable.
Delegate work to sub-agents and reason about when orchestration beats a single agent.
Wire custom tools, MCP servers, and skills into an Agent SDK application.
Pre-Reading Check — From Hand-Rolled Loop to the Agent SDK
1. What is the central problem the Claude Agent SDK is designed to solve?
It replaces the Messages API with a faster inference endpoint.
It spares you from re-implementing the agent loop, context management, sessions, and permissions for every project.
It removes the need for an API key by using claude.ai login for all products.
It trains a custom model on your codebase.
2. In the Claude Agent SDK, who runs the tool loop and where does session state live?
Anthropic runs the loop in a hosted container; state lives on Anthropic's infrastructure.
You hand-roll the loop; state lives wherever you put it.
The SDK runs the loop inside your own process; state is stored as JSONL on your filesystem.
The browser runs the loop; state lives in local storage.
3. What is the essential difference between query() and ClaudeSDKClient?
query() is a stateless call for a self-contained task; ClaudeSDKClient is a stateful object that retains conversation context across turns.
query() is for TypeScript only; ClaudeSDKClient is for Python only.
query() can use tools; ClaudeSDKClient cannot.
query() ends after a single model turn; ClaudeSDKClient ends after two.
4. Why does the SDK let the agent read files, run shell commands, and search the web with no executor code from you?
Because it ships a set of built-in tools (Read, Write, Edit, Bash, Grep, WebSearch, etc.) the agent can use immediately.
Because it silently uploads your files to Anthropic for processing.
Because every tool call is simulated rather than really executed.
Because it generates and compiles a new executor on each request.
5. What is the relationship between the Agent SDK and Claude Code?
They are unrelated products with separate loops and tools.
The Agent SDK is Claude Code refactored into a library, sharing the same loop, tools, and config conventions.
Claude Code is built on the Agent SDK but shares none of its tools.
The Agent SDK is a lightweight demo that cannot run Claude Code's tools.
1. From Hand-Rolled Loop to the Agent SDK
Key Points
The Claude Agent SDK (formerly Claude Code SDK) packages Claude Code's loop, built-in tools, and context management as a Python/TypeScript library that runs inside your own process.
It sits between the hand-rolled Client SDK (you run the loop) and Managed Agents (Anthropic runs everything) — the SDK runs the loop for you but on your infrastructure, with sessions stored as JSONL on your filesystem.
Use query() for a self-contained task and ClaudeSDKClient for an ongoing multi-turn conversation.
Behavior is configured through ClaudeAgentOptions — start with system_prompt and allowed_tools.
Built-in tools (Read, Write, Edit, Bash, Grep, WebSearch, WebFetch, AskUserQuestion…) are usable the moment you list them, eliminating most Client-SDK boilerplate.
By this point you have hand-assembled the agent loop: call the Messages API, inspect stop_reason, dispatch each tool_use block, splice the tool_result back, and loop. Building it once teaches you the mechanics — but re-implementing it plus context management, session persistence, permission gating, and sub-agent orchestration for every project is undifferentiated plumbing. The Agent SDK is about not rewriting that plumbing.
It helps to locate the SDK among three surfaces that are easy to confuse.
Three ways to run the agent loop.
Surface
Who runs the loop
Who executes tools
Where state lives
Reach for it when
Anthropic Client SDK (anthropic)
You (hand-rolled)
You
Wherever you put it
You need total control of every turn
Claude Agent SDK (claude-agent-sdk)
The SDK, in your process
SDK built-ins + your custom tools
JSONL on your filesystem
You want the Claude Code loop as a library
Managed Agents (REST)
Anthropic
Anthropic's container
Anthropic's infrastructure
You want a hosted, sandboxed workspace
Figure 5.2: Hand-rolled loop vs. the Agent SDK — who owns each responsibility.
Install with pip install claude-agent-sdk (Python 3.10+). Authenticate with ANTHROPIC_API_KEY or Bedrock/Vertex/Foundry variables. One operational note: third-party products built on the SDK must use API-key authentication — claude.ai login is not available for them.
The primary entry point is the query() async generator, which yields four typed message types — AssistantMessage, UserMessage, SystemMessage, ResultMessage. Within a single query() call the agent already takes as many turns as it needs; permission prompts and AskUserQuestion are handled in-loop and do not end the call. Behavior is configured through ClaudeAgentOptions:
Frequently used ClaudeAgentOptions fields.
Field
Purpose
system_prompt
Standing instructions defining role and constraints, sent on every turn
allowed_tools
Allowlist: listed tools are auto-approved with no prompt
disallowed_tools
Blocklist: named tools are refused outright
permission_mode
How unlisted tools are handled (e.g. 'acceptEdits')
max_turns
Hard ceiling on agent turns
mcp_servers / agents
MCP servers and sub-agent definitions
cwd / setting_sources / resume
Working dir (and session location), which config dirs load, session re-entry
[Animation slot: side-by-side walkthrough of the hand-rolled stop_reason loop collapsing into a single query() call]
A query() call is self-contained. For a multi-turn conversation in one process, Python offers ClaudeSDKClient, an async context manager that holds session state internally — each client.query() automatically continues the same session, so a later "refactor it" already knows what "it" refers to. In one sentence: query() is a stateless function call for a self-contained task; ClaudeSDKClient is a stateful object for an ongoing conversation.
If the SDK feels like Claude Code, that is because it is Claude Code, refactored into a library — same tools, same loop, same context management. Practically, it auto-loads Claude Code's config conventions (CLAUDE.md, .claude/ directories), which we exploit for memory next.
Post-Reading Check — From Hand-Rolled Loop to the Agent SDK
1. What is the central problem the Claude Agent SDK is designed to solve?
It replaces the Messages API with a faster inference endpoint.
It spares you from re-implementing the agent loop, context management, sessions, and permissions for every project.
It removes the need for an API key by using claude.ai login for all products.
It trains a custom model on your codebase.
2. In the Claude Agent SDK, who runs the tool loop and where does session state live?
Anthropic runs the loop in a hosted container; state lives on Anthropic's infrastructure.
You hand-roll the loop; state lives wherever you put it.
The SDK runs the loop inside your own process; state is stored as JSONL on your filesystem.
The browser runs the loop; state lives in local storage.
3. What is the essential difference between query() and ClaudeSDKClient?
query() is a stateless call for a self-contained task; ClaudeSDKClient is a stateful object that retains conversation context across turns.
query() is for TypeScript only; ClaudeSDKClient is for Python only.
query() can use tools; ClaudeSDKClient cannot.
query() ends after a single model turn; ClaudeSDKClient ends after two.
4. Why does the SDK let the agent read files, run shell commands, and search the web with no executor code from you?
Because it ships a set of built-in tools (Read, Write, Edit, Bash, Grep, WebSearch, etc.) the agent can use immediately.
Because it silently uploads your files to Anthropic for processing.
Because every tool call is simulated rather than really executed.
Because it generates and compiles a new executor on each request.
5. What is the relationship between the Agent SDK and Claude Code?
They are unrelated products with separate loops and tools.
The Agent SDK is Claude Code refactored into a library, sharing the same loop, tools, and config conventions.
Claude Code is built on the Agent SDK but shares none of its tools.
The Agent SDK is a lightweight demo that cannot run Claude Code's tools.
Pre-Reading Check — Memory and Context Management
1. How does the chapter distinguish short-term context from durable memory?
Short-term context is the whiteboard (live, bounded conversation); durable memory is the filing cabinet (files written to disk that survive).
Short-term context is on disk; durable memory is in the model's weights.
They are the same thing under two names.
Short-term context survives forever; durable memory is cleared each turn.
2. What does a session persist, and what does it not?
It persists the filesystem state, not the conversation.
It persists the conversation (as JSONL), not the filesystem — resuming does not un-edit files.
It persists only the system prompt.
It persists nothing; sessions are purely in-memory.
3. What is the difference between compaction and context editing?
Compaction summarizes stale history into a compact form; context editing clears stale tool results and thinking blocks outright.
They are identical; the terms are interchangeable.
Compaction runs client-side; context editing runs on the model's weights.
4. Why do compaction and prompt caching work together instead of against each other?
Compaction disables caching so every turn is billed at full price.
Compaction reuses the exact same system prompt, tools, and context prefix, so the strict prefix-match cache survives the compaction boundary.
Prompt caching stores the full transcript, making compaction unnecessary.
Caching only applies to sub-agents, never to the main session.
5. Which practice silently defeats prompt caching?
Keeping the system prompt and tool list byte-stable across turns.
Embedding datetime.now() in the system prompt or varying the tool set per request.
Writing learnings to a CLAUDE.md memory file.
Setting a max_turns ceiling.
2. Memory and Context Management
Key Points
Short-term context is the whiteboard — the live, window-bounded conversation, much of which goes stale. Durable memory is the filing cabinet — files (CLAUDE.md) written to disk that survive beyond a run and beyond summarization.
A session is the conversation on disk, written automatically as JSONL under ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl. It persists the conversation, not the filesystem.
Return to prior work three ways: continue (most recent session), resume (specific ID), or fork (diverging copy).
Compaction summarizes older history server-side near the limit; context editing clears stale results instead. Summarize vs. delete.
Compaction reuses the exact parent prefix so prompt caching survives the boundary — keep the system prompt and tool set byte-stable, or a datetime.now() defeats the cache entirely.
Long-running agents face a finite context window: every turn adds to it, so the conversation can overflow and cost can balloon as the growing history is re-sent. The SDK addresses both, but separate two ideas first. Cross-session memory in the SDK is file-based, built on Claude Code's convention: a CLAUDE.md (or .claude/CLAUDE.md) acts as a persistent scratchpad the SDK loads automatically from .claude/ and ~/.claude/. The recommended pattern is deliberate: rather than loading everything up front, the agent records what it learns into memory files and reads them back on demand.
Continue vs. resume vs. fork.
Mechanism
Field
Behavior
Typical use
Continue
continue_conversation=True
Resumes the most recent session in the directory — no ID needed
Pick up where you left off
Resume
resume=session_id
Returns to a specific past session by ID
Follow up; recover from error_max_turns with a higher limit
Fork
fork_session=True (with resume)
Copies the original's history into a new, diverging session
Explore an alternative while keeping the original intact
Figure 5.5: The context and memory lifecycle across a long-running session.
flowchart TD
A["Turn adds messages, tool calls, results"] --> B["Short-term context grows"]
B --> C{"Approaching context-window limit?"}
C -->|no| A
C -->|yes| D["Compaction summarizes older history server-side"]
D --> E["Reuses exact parent prefix: system prompt + tools + context"]
E --> F["Prompt caching survives the compaction boundary"]
F --> A
B --> G["Agent writes learnings to CLAUDE.md / memory dir"]
G --> H["Durable memory on disk"]
H --> I["Read back on demand; survives summarization"]
I --> A
D -.->|"gist kept, detail lost"| J["Anything unaffordable to lose belongs in memory, not transcript"]
Prompt caching serves a repeated request prefix from cache at roughly one-tenth the base input price — but as a strict prefix match, any byte change invalidates everything after it. This is exactly why compaction and caching are designed together: during compaction the API reuses the same system prompt, tool definitions, and context, so the cache survives the boundary. The pitfall to internalize is silent cache invalidators — a datetime.now() in the system prompt, or a tool set that varies per request, defeats caching entirely.
Compaction and memory play different roles.
Technique
What it does
What survives
Compaction
Summarizes older history server-side near the limit
The summary (gist)
Context editing
Clears stale tool results / thinking blocks
The remaining transcript
File-based memory
Writes learnings to CLAUDE.md / memory dir
The written files (survive summarization)
[Animation slot: a growing transcript hitting the window limit, compaction collapsing old turns to a summary, while a CLAUDE.md file persists untouched off to the side]
Anthropic's guidance for long-running agents is to use both: compaction keeps active context small, while file-based memory preserves what must survive summarization. Compaction is lossy by design — anything you cannot afford to lose belongs in a memory file, not the transcript.
Post-Reading Check — Memory and Context Management
1. How does the chapter distinguish short-term context from durable memory?
Short-term context is the whiteboard (live, bounded conversation); durable memory is the filing cabinet (files written to disk that survive).
Short-term context is on disk; durable memory is in the model's weights.
They are the same thing under two names.
Short-term context survives forever; durable memory is cleared each turn.
2. What does a session persist, and what does it not?
It persists the filesystem state, not the conversation.
It persists the conversation (as JSONL), not the filesystem — resuming does not un-edit files.
It persists only the system prompt.
It persists nothing; sessions are purely in-memory.
3. What is the difference between compaction and context editing?
Compaction summarizes stale history into a compact form; context editing clears stale tool results and thinking blocks outright.
They are identical; the terms are interchangeable.
Compaction runs client-side; context editing runs on the model's weights.
4. Why do compaction and prompt caching work together instead of against each other?
Compaction disables caching so every turn is billed at full price.
Compaction reuses the exact same system prompt, tools, and context prefix, so the strict prefix-match cache survives the compaction boundary.
Prompt caching stores the full transcript, making compaction unnecessary.
Caching only applies to sub-agents, never to the main session.
5. Which practice silently defeats prompt caching?
Keeping the system prompt and tool list byte-stable across turns.
Embedding datetime.now() in the system prompt or varying the tool set per request.
Writing learnings to a CLAUDE.md memory file.
Setting a max_turns ceiling.
Pre-Reading Check — Sub-Agents and Orchestration
1. In the Agent SDK, what must be true for an orchestrator to be able to invoke sub-agents?
The Agent tool must appear in the orchestrator's allowed_tools.
The sub-agents must each have Bash in their tool set.
The orchestrator must run in a hosted Managed Agents container.
Compaction must be disabled first.
2. Why is the isolation of a sub-agent's context window described as "the point"?
It lets the sub-agent write to the orchestrator's files directly.
The sub-agent acts as an intelligent filter, returning only condensed findings so raw data never floods the orchestrator's context.
It doubles the size of the orchestrator's context window.
It guarantees the sub-agent uses the same model as the orchestrator.
3. What does the orchestrator-worker pattern's canonical shape look like?
A lead agent decomposes and spawns parallel sub-agents in isolated contexts, then synthesizes and verifies their condensed findings.
A single agent processes every subtask sequentially in one context window.
Sub-agents talk directly to the user and skip the orchestrator.
Each sub-agent must finish before the next one is spawned.
4. According to Anthropic's numbers, why should multi-agent orchestration be reserved for high-value work?
Multi-agent systems use roughly 15× the tokens of chat, and token usage explained ~80% of performance variance.
Multi-agent systems are slower on every task by design.
Sub-agents cannot use any tools, limiting their usefulness.
Orchestrators can only spawn one sub-agent at a time.
5. When does the multi-agent pattern fit poorly?
When the task is broad, breadth-first, and parallelizable.
When agents must share the same context or there are many inter-agent dependencies (sequential, context-shared work).
When you want to explore multiple independent research directions.
Whenever you need more than one specialized prompt.
3. Sub-Agents and Orchestration
Key Points
Sub-agents are declared with AgentDefinition and passed via the agents option; they are invoked through the built-in Agent tool — which must be listed in allowed_tools.
Each sub-agent gets a restricted tools set (e.g. a read-only reviewer that physically cannot edit) and runs in its own isolated context window, returning only its final message.
The sub-agent is an intelligent filter: raw data never floods the orchestrator; only condensed findings return. Messages carry parent_tool_use_id for tracing.
The orchestrator-worker pattern (decompose → spawn parallel workers → synthesize → verify) wins on breadth: a 90.2% gain over single-agent on research evals; benefits are parallelization, specialization, escalation.
The cost is real: agents use ~4× chat tokens, multi-agent ~15× — so reserve it for high-value, parallelizable work and prefer a single agent for sequential or context-shared tasks.
A single agent working a broad problem hits a wall: one context window holds only so much, and stuffing it with raw search results and dead ends crowds out the reasoning you want. Sub-agents let one agent delegate focused work to others in isolated context windows that report back only conclusions. In the SDK, each sub-agent gets a restricted tools set — a code-reviewer that can Read, Glob, and Grep but not Write or Edit physically cannot modify the code it reviews.
Think of the orchestrator as a manager who assigns a researcher a question and receives back a one-page memo — not the researcher's entire pile of notes and browser tabs. The canonical shape is orchestrator-worker: a lead agent coordinates while delegating to specialized sub-agents that operate in parallel.
Figure 5.7: The orchestrator-worker pattern.
flowchart TD
O["Orchestrator: decompose, spawn, monitor, synthesize"]
O --> W1["Sub-agent A (isolated context)"]
O --> W2["Sub-agent B (isolated context)"]
O --> W3["Sub-agent C (isolated context)"]
W1 -->|condensed findings only| V["Verification pass: cross-check outputs"]
W2 -->|condensed findings only| V
W3 -->|condensed findings only| V
V --> R["Synthesized result to user"]
Delegation succeeds or fails on the hand-off. Each sub-agent needs an objective, an output format, guidance on tools and sources, and clear task boundaries. Vague delegation ("go look into the auth module") produces overlapping, redundant sub-agents; precise delegation ("identify every place session_token is validated, return file:line locations, use only Read and Grep") produces clean, composable output. Anthropic also embeds effort-scaling rules so the orchestrator right-sizes fan-out:
Effort scaling in the orchestrator prompt.
Task type
Sub-agents
Tool calls each
Simple fact-finding
1
3–10
Direct comparisons
2–4
10–15
Complex research
10+ (divided responsibilities)
—
[Animation slot: an orchestrator fanning out three sub-agent bubbles in parallel, each shrinking a big pile of raw data down to a small memo before it returns]
The payoff, when the task fits, is substantial: a Claude Opus 4 lead coordinating Claude Sonnet 4 sub-agents outperformed a single-agent Opus 4 by 90.2% on internal research evals, and parallelization cut research time by up to ~90%. But the cost is real — agents use ~4× chat tokens and multi-agent systems ~15×, with token usage explaining ~80% of performance variance. The pattern fits poorly where agents share context or have many inter-agent dependencies: if sub-agents would mostly wait on each other or re-read shared state, you are paying the 15× multiplier for coordination overhead, not parallelism. A single agent is then the better choice.
Post-Reading Check — Sub-Agents and Orchestration
1. In the Agent SDK, what must be true for an orchestrator to be able to invoke sub-agents?
The Agent tool must appear in the orchestrator's allowed_tools.
The sub-agents must each have Bash in their tool set.
The orchestrator must run in a hosted Managed Agents container.
Compaction must be disabled first.
2. Why is the isolation of a sub-agent's context window described as "the point"?
It lets the sub-agent write to the orchestrator's files directly.
The sub-agent acts as an intelligent filter, returning only condensed findings so raw data never floods the orchestrator's context.
It doubles the size of the orchestrator's context window.
It guarantees the sub-agent uses the same model as the orchestrator.
3. What does the orchestrator-worker pattern's canonical shape look like?
A lead agent decomposes and spawns parallel sub-agents in isolated contexts, then synthesizes and verifies their condensed findings.
A single agent processes every subtask sequentially in one context window.
Sub-agents talk directly to the user and skip the orchestrator.
Each sub-agent must finish before the next one is spawned.
4. According to Anthropic's numbers, why should multi-agent orchestration be reserved for high-value work?
Multi-agent systems use roughly 15× the tokens of chat, and token usage explained ~80% of performance variance.
Multi-agent systems are slower on every task by design.
Sub-agents cannot use any tools, limiting their usefulness.
Orchestrators can only spawn one sub-agent at a time.
5. When does the multi-agent pattern fit poorly?
When the task is broad, breadth-first, and parallelizable.
When agents must share the same context or there are many inter-agent dependencies (sequential, context-shared work).
When you want to explore multiple independent research directions.
Whenever you need more than one specialized prompt.
Pre-Reading Check — Integrating Tools, MCP, and Skills
1. How do your own domain capabilities enter an SDK agent?
Through the mcp_servers option on ClaudeAgentOptions, so their tools join the built-ins.
By retraining the model with a fine-tuning run.
Only by editing the built-in tool source code.
They cannot be added; only built-in tools are available.
2. How does an SDK agent load skills, and what disclosure model do they follow?
Skills are hard-coded in the system prompt and always fully loaded.
Skills load through the .claude/ setting sources; a short description sits in context and the full SKILL.md is read only when the task calls for it.
Skills must be passed inline as a base64 blob on every request.
Skills are downloaded from Anthropic's servers per turn.
3. What is the key mental model for allowed_tools in the permission model?
It is an allowlist that suppresses prompts; a tool not on it is not forbidden, it falls through to permission_mode and any can_use_tool callback.
Any tool not in allowed_tools is automatically blocked forever.
It is identical in effect to disallowed_tools.
It only applies to sub-agents, not the main agent.
4. If you want a tool categorically refused, what should you do?
Leave it out of allowed_tools and assume that blocks it.
Name it in disallowed_tools (or gate it with a can_use_tool callback).
Lower max_turns to zero.
Set permission_mode to 'acceptEdits'.
5. In the end-to-end assistant, why is Bash placed in disallowed_tools while Read and Edit are in allowed_tools?
Because Bash is not a real SDK tool.
To demonstrate the safe production posture: auto-approve low-risk actions, block irreversible ones like shell access outright.
Because Bash would defeat prompt caching.
Because sub-agents can never use Bash.
4. Integrating Tools, MCP, and Skills
Key Points
Custom capabilities and third-party systems enter through the mcp_servers option — the same Model Context Protocol, now an SDK configuration point; their tools join the built-ins.
Skills (folders with a SKILL.md) load through the .claude/ setting sources with the same progressive disclosure: a short description in context, full SKILL.md read on demand.
The permission model is layered: allowed_tools auto-approves (suppresses prompts), unlisted tools fall through to permission_mode and a can_use_tool callback, and disallowed_tools refuses outright.
Key mental model: allowed_tools is an allowlist that suppresses prompts, not a list of the only permitted tools — to categorically refuse a tool, name it in disallowed_tools.
Assembled together, these fields turn ClaudeAgentOptions into a complete, production-shaped assistant with a stable system prompt, an allowlist, a read-only reviewer sub-agent, and file-based memory.
The built-in tools cover reading, editing, running commands, and searching the web. Real applications need more: your own domain functions, third-party systems reached through MCP servers, and task-specific skills. You register MCP servers on ClaudeAgentOptions via mcp_servers, and their tools become available alongside the built-ins. Because the SDK follows Claude Code's conventions, a server already configured for Claude Code is reachable the same way. Skills load through the same .claude/ configuration the SDK reads on startup, using the familiar progressive-disclosure model.
Figure 5.9: How tools, MCP servers, and skills register into an SDK agent.
Guardrails are layered, and understanding the layering prevents both accidental blocks and accidental grants:
The permission model (most permissive to most restrictive).
Layer
Effect
allowed_tools
Named tools are auto-approved — no prompt, they just run
permission_mode (e.g. 'acceptEdits')
How tools not in allowed_tools are handled
can_use_tool callback
Programmatic decision for tools that fall through
disallowed_tools
Named tools are blocked outright
The key mental model: allowed_tools is an allowlist that suppresses prompts, not merely a list of permitted tools. A tool not on the list is not forbidden — it falls through to permission_mode and, if configured, a can_use_tool callback. To categorically refuse a tool, name it in disallowed_tools. The safest production posture is an explicit allowlist for read-only, low-risk actions combined with a blocklist (or a can_use_tool gate) for anything irreversible.
The minimal end-to-end assistant assembles everything: a custom system prompt kept byte-stable, an allowed_tools allowlist with Agent included, Bash blocked via disallowed_tools, a read-only code-reviewersub-agent, cross-session memory via CLAUDE.md loaded from the setting sources, and automatic session persistence, compaction, and prompt caching from the SDK. Across two query() turns, "it" resolves to auth.py from retained context, the system prompt drives a delegated review in an isolated context window, and the cached prefix is reused because the prompt and tool set never interpolate volatile values.
[Animation slot: the ClaudeAgentOptions object at the center with system_prompt, allowed_tools, disallowed_tools, agents, and setting_sources snapping into place, then a two-turn conversation running through it]
Post-Reading Check — Integrating Tools, MCP, and Skills
1. How do your own domain capabilities enter an SDK agent?
Through the mcp_servers option on ClaudeAgentOptions, so their tools join the built-ins.
By retraining the model with a fine-tuning run.
Only by editing the built-in tool source code.
They cannot be added; only built-in tools are available.
2. How does an SDK agent load skills, and what disclosure model do they follow?
Skills are hard-coded in the system prompt and always fully loaded.
Skills load through the .claude/ setting sources; a short description sits in context and the full SKILL.md is read only when the task calls for it.
Skills must be passed inline as a base64 blob on every request.
Skills are downloaded from Anthropic's servers per turn.
3. What is the key mental model for allowed_tools in the permission model?
It is an allowlist that suppresses prompts; a tool not on it is not forbidden, it falls through to permission_mode and any can_use_tool callback.
Any tool not in allowed_tools is automatically blocked forever.
It is identical in effect to disallowed_tools.
It only applies to sub-agents, not the main agent.
4. If you want a tool categorically refused, what should you do?
Leave it out of allowed_tools and assume that blocks it.
Name it in disallowed_tools (or gate it with a can_use_tool callback).
Lower max_turns to zero.
Set permission_mode to 'acceptEdits'.
5. In the end-to-end assistant, why is Bash placed in disallowed_tools while Read and Edit are in allowed_tools?
Because Bash is not a real SDK tool.
To demonstrate the safe production posture: auto-approve low-risk actions, block irreversible ones like shell access outright.