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=optionalleaves it out. Reinstall with optional dependencies, or install Claude Code yourself and pointpathToClaudeCodeExecutableat 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:
| Provider | Environment variables | Guide |
|---|---|---|
| Amazon Bedrock | CLAUDE_CODE_USE_BEDROCK=1 plus AWS credentials | Amazon Bedrock |
| Claude Platform on AWS | CLAUDE_CODE_USE_ANTHROPIC_AWS=1, ANTHROPIC_AWS_WORKSPACE_ID plus AWS credentials | Claude Platform on AWS |
| Google Cloud's Agent Platform | CLAUDE_CODE_USE_VERTEX=1 plus Google Cloud credentials | Google Vertex AI |
| Microsoft Foundry | CLAUDE_CODE_USE_FOUNDRY=1 plus Azure credentials | Microsoft 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:
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.- The prompt states the outcome. You do not tell Claude which tools to use; it decides.
- The options shape behaviour.
allowedToolspre-approvesRead,EditandGlob, andpermissionMode: "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
| Setup | Command |
|---|---|
| TypeScript | npx tsx agent.ts (or npx tsx agent.mts) |
| Python with uv | uv run agent.py |
| Python with pip | python 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 inorInvalid API keynearly always meansANTHROPIC_API_KEYis not set in the shell that launched the script. Remember that.envfiles 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
| Tools | What the agent can do |
|---|---|
Read, Glob, Grep | Look but not touch: audits and reports |
Read, Edit, Glob | Read and change existing files |
Read, Edit, Bash, Glob, Grep | Change 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.