Skip to content

Agent SDK quickstart

Install the Python or TypeScript Agent SDK, set your credentials and run a first agent that tidies up a small script on its own.

This walkthrough gets an agent running in about ten minutes. You will set up a project, give the agent a small script with a couple of problems, and watch it read, reason about and edit the file without you touching it.

Before you start

  • Node.js 18 or later, or Python 3.10 or later.
  • An Anthropic account with an API key from the Claude Console (or credentials for one of the cloud providers listed below).

Set up a project

Make a folder to work in. By default the agent can see the directory it runs in and everything beneath it, so keep experiments in their own folder.

mkdir invoice-agent && cd invoice-agent

TypeScript, new project

npm init -y
npm pkg set type=module
npm install @anthropic-ai/claude-agent-sdk
npm install --save-dev tsx

Setting "type": "module" means top-level await works in your script, and tsx runs .ts files without a build step.

TypeScript, existing project

npm install @anthropic-ai/claude-agent-sdk
npm install --save-dev tsx

If the project is CommonJS, name the script agent.mts rather than agent.ts. The .mts extension tells tsx to treat that one file as an ES module, so you get top-level await without converting the whole codebase.

Python with uv

uv init
uv add claude-agent-sdk

Python with pip

macOS or Linux:

python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk

Windows PowerShell:

py -m venv .venv
.venv\Scripts\Activate.ps1
pip install claude-agent-sdk

If PowerShell refuses to run Activate.ps1 because of the execution policy, run Set-ExecutionPolicy -Scope Process RemoteSigned in that window first.

Note: Both SDKs include a native Claude Code binary, so you usually do not need Claude Code installed separately. Two exceptions: if pip falls back to the Python source distribution (ARM64 Windows is one case), there is no bundled binary, so install Claude Code natively and the SDK will find it on your PATH. And because the TypeScript SDK pulls its binary in as an npm optional dependency, npm ci --omit=optional leaves it out. Reinstall with optional dependencies, or install Claude Code yourself and point pathToClaudeCodeExecutable at it. See setup for native installs.

Set your credentials

Put your API key in the environment of the shell that will run the agent:

export ANTHROPIC_API_KEY=sk-ant-...
$env:ANTHROPIC_API_KEY = "sk-ant-..."

The SDK reads the process environment only. It does not load .env files for you, so if you keep secrets there, load them yourself (for example with dotenv or python-dotenv) before calling the SDK.

To use a cloud provider instead of the Anthropic API, set the matching switch and configure that provider's credentials as normal:

ProviderEnvironment variablesGuide
Amazon BedrockCLAUDE_CODE_USE_BEDROCK=1 plus AWS credentialsAmazon Bedrock
Claude Platform on AWSCLAUDE_CODE_USE_ANTHROPIC_AWS=1, ANTHROPIC_AWS_WORKSPACE_ID plus AWS credentialsClaude Platform on AWS
Google Cloud's Agent PlatformCLAUDE_CODE_USE_VERTEX=1 plus Google Cloud credentialsGoogle Vertex AI
Microsoft FoundryCLAUDE_CODE_USE_FOUNDRY=1 plus Azure credentialsMicrosoft Foundry

Note: Third-party developers may not offer claude.ai login or claude.ai rate limits in their products unless Anthropic has approved it. Stick to API keys or cloud provider credentials.

Give the agent something to fix

Create invoices.py with two functions that will fall over on awkward input:

def vat_inclusive(net_amounts, rate):
    return [round(n * (1 + rate), 2) for n in net_amounts]


def average_invoice(totals):
    return sum(totals) / len(totals)


def client_initials(client):
    first, last = client["name"].split(" ")
    return first[0] + last[0]

average_invoice([]) divides by zero, and client_initials breaks on a single-word name or a missing client.

Write the agent

Python, in agent.py:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage


async def main():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Edit", "Glob"],
        permission_mode="acceptEdits",
    )
    task = "Find inputs that would crash the functions in invoices.py and make them safe."

    async for message in query(prompt=task, options=options):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if hasattr(block, "text"):
                    print(block.text)
                elif hasattr(block, "name"):
                    print(f"-> {block.name}")
        elif isinstance(message, ResultMessage):
            print(f"Finished: {message.subtype}")


asyncio.run(main())

TypeScript, in agent.ts (or agent.mts):

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

const task = "Find inputs that would crash the functions in invoices.py and make them safe.";

for await (const message of query({
  prompt: task,
  options: {
    allowedTools: ["Read", "Edit", "Glob"],
    permissionMode: "acceptEdits",
  },
})) {
  if (message.type === "assistant") {
    for (const block of message.message.content) {
      if (block.type === "text") console.log(block.text);
      if (block.type === "tool_use") console.log(`-> ${block.name}`);
    }
  } else if (message.type === "result") {
    console.log(`Finished: ${message.subtype}`);
  }
}

Three things are doing the work here:

  1. query() starts the agent loop and returns an async iterator. Each item is a message: Claude's text, a tool call, a tool result, a system event or the final result.
  2. The prompt states the outcome. You do not tell Claude which tools to use; it decides.
  3. The options shape behaviour. allowedTools pre-approves Read, Edit and Glob, and permissionMode: "acceptEdits" lets file edits through without a prompt. The full list lives in configure your agent.

The loop keeps yielding until Claude finishes or hits a limit or error. The if checks filter the stream down to readable output; without them you would also see initialisation messages and other internals, which are handy when debugging.

Run it

SetupCommand
TypeScriptnpx tsx agent.ts (or npx tsx agent.mts)
Python with uvuv run agent.py
Python with pippython agent.py with the virtual environment active

You should see Claude explain what it found, a few -> Read and -> Edit lines, and then Finished: success. Open invoices.py and you will find guards for the empty list and the odd client names.

Tip: An error such as Not logged in or Invalid API key nearly always means ANTHROPIC_API_KEY is not set in the shell that launched the script. Remember that .env files are not read automatically. The errors reference has more on authentication failures.

Try a few variations

Prompts to try against the same file:

  • "Add type hints and docstrings to every function in invoices.py"
  • "Write a short README describing what invoices.py does"
  • "Write pytest tests for invoices.py, run them and fix anything that fails" (needs Bash, see below)

Options to experiment with, all on the same options object:

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Edit", "Glob", "Bash", "WebSearch"],
    permission_mode="acceptEdits",
    system_prompt="You are a careful Python reviewer. Prefer explicit errors over silent defaults.",
)

Adding Bash lets the agent run tests and scripts. Adding WebSearch lets it look things up. system_prompt changes its persona and priorities (see modifying system prompts for the trade-offs).

Picking a tool set

ToolsWhat the agent can do
Read, Glob, GrepLook but not touch: audits and reports
Read, Edit, GlobRead and change existing files
Read, Edit, Bash, Glob, GrepChange files and run commands: close to full autonomy

Permission modes control how much oversight you keep on top of that. The SDK combines the mode with your allow and deny rules in a fixed order; SDK permissions explains the evaluation and the agent loop page summarises each mode.

This example streams so you can watch progress. For a CI job or background worker you might simply wait for the result message; streaming input versus single messages explains the two input styles.