Skip to content
datarekha

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 Last reviewed

12 min read Intermediate Agentic AI Lesson 47 of 78

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:

① Gather contextread files · search · recall② Take actionedit · run · call tools③ Verify workrun tests · read outputloop until the work checks out
Gather context, act, then verify — an application pattern the SDK makes possible. The verify step is what separates a reliable agent from a confident-but-wrong one.

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.

  1. 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.
  2. 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.
  3. 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 write report.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, read AGENTS.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 of 2599 must 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:

SituationBetter starting pointWhy
The next action depends on files or command outputClaude Agent SDKThe agent can inspect, act, and verify in one environment
Stages and transitions are known in advanceLangGraph or a workflow engineControl flow is visible and enforceable
Several roles must debate or hand off workCrew-style orchestrationRole boundaries are the primary abstraction
The task is one fixed transformationA normal program or direct model callAn 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

0/3
Q1What is the Claude Agent SDK?
Q2Why does verification change the reliability of an agent?
Q3Transfer: an agent must update production Terraform, but it currently has Read and Bash access in a broad workspace. What is the safest next design change?

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.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
What is an AI agent, and how does it differ from a single LLM call?

An 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.

How do function/tool calling and LLM agents work at a high level?

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.

What is the confused deputy problem in agent systems, and how does it relate to agent-to-agent authentication?

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.

How would you prevent an AI agent from leaking or misusing API credentials?

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.

Related lessons

Explore further