Claude Agent SDK
The Claude Agent SDK gives Python and TypeScript agents the harness behind Claude Code: a real tool loop, filesystem, shell, MCP, subagents, context management, and permissions.
Written and reviewed by Shreyash Prashu Last reviewed
What you'll learn
- What the Claude Agent SDK is: Claude Code's agent harness, available standalone
- Why gather, act, and verify form a useful production loop
- How tools, MCP, skills, subagents, permissions, and context compaction fit together
- How to constrain a coding or data agent without mistaking tool permissions for a security boundary
- When the SDK is a better fit than LangGraph, a workflow engine, or a role-based crew
Before you start
At 3 a.m., a useful coding agent is not answering a question. It is opening a repository, finding the relevant files, editing three of them, running the tests, reading a traceback, and trying again. It may need to inspect a shell script before it knows which command to run. It may need to search the web for a library’s current API. Then it must leave the working tree in a state another engineer can trust.
That is a very different problem from sending a prompt to a model and printing its answer.
The Claude Agent SDK is the standalone agent harness behind Claude Code. A harness runs the model, exposes tools, manages conversation state, applies permissions, and keeps work moving until a stopping point. You can use it to build coding, DevOps, or data agents instead of assembling the loop from API calls.
What you’re actually getting
The SDK provides an opinionated working environment:
- A real filesystem and shell for inspecting and changing files and running commands.
- Built-in tools for reading, writing, editing, shell commands, web search, and web fetching, depending on configuration.
- MCP, the Model Context Protocol, for connecting services such as databases, issue trackers, and deployment systems as tools.
- Skills and project instructions, including supported instruction files and the AGENTS.md pattern when explicitly read or configured for the installed version.
- Subagents for delegating bounded, separable investigations.
- Permissions for restricting or approving tools and risky actions.
- Context management, including compaction of long conversations.
It is available for Python and TypeScript. The key distinction is that your program drives a computer-using agent, not just a chat completion. The model chooses whether to read a file, run a command, or inspect a result. That flexibility helps it handle unfamiliar repositories, but requires boundaries and verification.
The loop an application can require
The SDK provides an iterative model-and-tool-use loop that makes a practical application pattern possible:
The SDK does not enforce these phases or repeat until an external check passes. Your prompt, validators, hooks, or supervising program must require verification and decide whether the run is acceptable.
- Gather context. Read relevant instructions, files, command output, and previous state. Avoid dumping an entire repository into context: irrelevant text consumes space and may contain contradictory instructions.
- Take action. Edit a file, run a command, call an MCP tool, or delegate work. These are effects in an environment, not merely words in a chat.
- Verify work. Run tests, inspect a diff, read output, or compare the result with an explicit requirement. If it fails, that observation becomes the next context and the loop continues.
The causal mechanism is simple: a model predicts its next action from the evidence in context. A test failure, changed file, or exit status supplies new evidence. Without it, the next prediction rests on an untested assumption. “Done” is a claim, not evidence.
One task, with the loop visible
Suppose an internal data agent must:
Read the three CSV files in
./data, calculate the monthly sales summary, and writereport.md.
The directory contains january.csv, february.csv, and march.csv, each with
4,000 data rows: 12,000 rows total. An AGENTS.md file says currency is stored in
cents, refunds must be shown separately, and the report needs four headings:
Summary, Revenue, Refunds, and Data quality.
That AGENTS.md file is a project convention. The SDK does not automatically load
filesystem instructions merely because the file exists. Configure the supported
instruction convention for your version, or tell the model to read it.
A sensible run is:
- Gather: list
./data, readAGENTS.md, and inspect CSV headers. Use a shell command or small script for totals rather than putting all 12,000 rows in the conversation. - Act: create
report.md. A source value of2599must appear as 25.99 dollars, not 2,599 dollars. Record that exactly three files and 12,000 rows were processed. - Verify: check that the report exists, contains all four headings, and reports 12,000 rows. Run the repository validator. If Refunds is missing, the failure gives the agent a specific repair target.
The SDK supplies the loop and tools; it does not know the currency rule, required headings, or what makes a report acceptable. Put those acceptance criteria in the prompt, project instructions, tests, or tool contracts. A good task defines both the objective and the evidence of completion.
The shape of an agent
# requires: pip install claude-agent-sdk
import asyncio
from pathlib import Path
from claude_agent_sdk import (
ClaudeAgentOptions,
PermissionResultAllow,
PermissionResultDeny,
query,
)
REPO = Path("/path/to/repository").resolve()
DATA = (REPO / "data").resolve()
REPORT = (REPO / "report.md").resolve()
INSTRUCTION_FILES = {
(REPO / "CLAUDE.md").resolve(),
(REPO / "AGENTS.md").resolve(),
}
# These are application-owned, trusted wrappers. Do not replace this exact-match
# policy with "any command beginning with python" or "any shell command".
SAFE_COMMANDS = {
"wc -l data/january.csv data/february.csv data/march.csv",
"./tools/summarize-csvs",
"./tools/validate-report",
}
def resolve_from_repo(path_text):
path = Path(path_text)
if not path.is_absolute():
path = REPO / path
return path.resolve()
def is_inside(path_text, root):
try:
resolve_from_repo(path_text).relative_to(root)
return True
except ValueError:
return False
async def can_use_tool(tool_name, input_data, context):
if tool_name == "Read":
path = resolve_from_repo(input_data.get("file_path", ""))
if is_inside(str(path), DATA) or path in INSTRUCTION_FILES:
return PermissionResultAllow()
return PermissionResultDeny(
message="Read is limited to the CSV directory and project instructions."
)
if tool_name in {"Write", "Edit"}:
path = resolve_from_repo(input_data.get("file_path", ""))
if path == REPORT:
return PermissionResultAllow()
return PermissionResultDeny(
message="Only report.md may be written or edited in this run."
)
if tool_name == "Bash":
command = input_data.get("command", "").strip()
if command in SAFE_COMMANDS:
return PermissionResultAllow()
return PermissionResultDeny(
message="This unattended run allows only the approved exact commands."
)
return PermissionResultDeny(
message=f"{tool_name} is not approved for this run."
)
async def main():
options = ClaudeAgentOptions(
system_prompt=(
"You are a careful data-engineering assistant. "
"Do not claim completion without checking the output."
),
cwd=str(REPO),
setting_sources=["project"],
disallowed_tools=["WebSearch"],
can_use_tool=can_use_tool,
permission_mode="default",
)
async for message in query(
prompt=(
"Summarize the CSVs in ./data and write report.md. "
"Read AGENTS.md explicitly before working; do not assume it was loaded "
"automatically. Use only the approved commands when a calculation or "
"validation is needed. Verify the report before you finish."
),
options=options,
):
print(message)
asyncio.run(main())
This one-shot program streams messages from query(). cwd makes ./data refer to
the intended repository, while setting_sources=["project"] loads project settings
and supported instructions. In the current convention, CLAUDE.md is automatically
loaded; this example asks the model to read AGENTS.md explicitly. Check the
documentation for your installed version.
The can_use_tool callback inspects arguments: reads are limited to data and
instruction files, writes to report.md, and Bash to three exact commands.
disallowed_tools=["WebSearch"] provides a hard denial. permission_mode="default"
does not create an approval screen; in this non-interactive program, the callback is
the policy. For an interactive session, use ClaudeSDKClient to keep sending and
receiving messages on one session.
The SDK’s exact option fields and tool names can change. It was formerly called the Claude Code SDK, and the Python and TypeScript APIs evolve. Check the official Anthropic docs before using a configuration in production.
What the built-in pieces are doing
The filesystem and shell make claims testable: files provide actual source, commands provide output and exit status, and edits produce inspectable diffs.
MCP extends this outside the local machine. A read-only warehouse server could provide only the data needed instead of a 4 GB export. It still needs authentication, input validation, and query limits; MCP simplifies connection, not trust.
Skills and project instructions carry durable local conventions, such as test
commands, generated directories, or currency rules. Configure cwd and
setting_sources=["project"]; use the supported CLAUDE.md convention for automatic
instructions, and explicitly read AGENTS.md when needed. Put immediate goals in the
prompt and machine-enforced requirements in tests or tool code. Instructions guide a
model but cannot enforce database permissions.
Subagents help with genuinely separable investigations, but add model calls, context-transfer problems, and conflicting conclusions. They are additional probabilistic workers whose results need verification. Context compaction summarizes old tool output so long runs can continue, but summaries are imperfect. Keep critical constraints short and write durable state to files or a database.
Because an unchecked action becomes the premise for the next action. If the agent edits the wrong configuration file, then treats that edit as truth, later actions can compound the mistake. Verification interrupts the chain.
Prefer evidence external to the model’s opinion:
- a test status or formatter result;
- a diff or database row count;
- human approval for a payment or production deployment.
The model can interpret these signals, but should not be their only source.
A production pattern that holds up
For the CSV agent, use a narrow workspace or container and a service account that can read inputs and write the report but cannot modify production data. Set the working directory explicitly.
Define completion evidence: three files processed, 12,000 rows counted, four
headings present, the validator succeeds, and no source CSV changed. Expose only the
needed tools, then separately decide which requests may be auto-approved. Use
can_use_tool for command- and path-level policy; do not treat “it might be useful”
as a security review.
Pause before risky effects, not after them. Save the final diff, validation output, tool calls, and approval decisions so an operator can determine what happened. For deeper tracing, see observability and tracing for agents.
Failure modes you will actually see
The agent says “Done,” but the report is wrong. An objective without an acceptance check produces a polished answer with no evidence. Add a validator or mechanical checklist and require it after the final edit.
A long run forgets a crucial rule. Context compaction or noisy output can bury requirements. Keep important rules and acceptance criteria short, repeat them near critical steps, and make validators check them. Store requirements outside chat when they must survive a restart.
Repository content gives a dangerous instruction. A README, fixture, or fetched page can contain prompt injection. Treat repository text as data, keep secrets out of the workspace, restrict network access, and require approval for external effects. The permission layer helps, but the OS sandbox and least-privilege account provide the stronger boundary.
For security details, including tool poisoning and least privilege, see Agent Security.
Where it fits
The practical question is who should control the next step:
| Situation | Better starting point | Why |
|---|---|---|
| The next action depends on files or command output | Claude Agent SDK | The agent can inspect, act, and verify in one environment |
| Stages and transitions are known in advance | LangGraph or a workflow engine | Control flow is visible and enforceable |
| Several roles must debate or hand off work | Crew-style orchestration | Role boundaries are the primary abstraction |
| The task is one fixed transformation | A normal program or direct model call | An open-ended loop adds cost and failure surface |
The SDK does not guarantee that an agent behaves exactly like Claude Code. Claude Code also has its own interface, defaults, and operating environment. You still own the prompt, workspace, credentials, approvals, tests, and monitoring.
Quick check
Quick check
Next
The same production concerns appear in every agent framework. Start with agent memory when you need an agent to retain useful state beyond its current run.
Practice this in an interview
All questionsAn AI agent is an application that lets an LLM choose and execute validated tools in a bounded loop, carrying observations and state forward until it reaches a goal or needs approval. A single LLM call produces one response or tool-call proposal and stops; it does not itself provide the loop, live-system access, memory, or side effects.
Tool calling extends the LLM's output space to include structured function invocations. The model emits a JSON object naming a tool and its arguments; the runtime executes the tool and feeds the result back as a new message. An agent is a loop that repeats this cycle — observe, think, act — until the task is complete or a stopping condition is met.
No. Agent-to-agent authentication identifies the calling agent, but it does not establish that the original user authorized the requested action. Preventing a confused deputy requires verified identity propagation, explicit delegation, and authorization at every hop.
Keep raw credentials outside model context and traces. Let the model propose typed intent, authorize the final action and arguments deterministically, then have a trusted executor inject a short-lived, narrowly scoped, audience-restricted credential for one call. Re-authorize downstream and gate high-impact writes with explicit approval.