Picking the right layer saves weeks: don't rebuild an agent harness that already exists, or adopt one you don't need.
There are three ways to build an agent on Claude, from most control to least work:
- Messages API (this course so far): you write the loop, the tools and the memory handling. Best for focused features and full control.
- Claude Agent SDK: a library (Python and TypeScript) that runs the same agent loop as Claude Code inside your process, with built-in tools for reading and editing files, running commands and searching the web, plus permissions, sessions (resumable conversations), hooks (your own code run at set points), subagents (helper agents for sub-tasks) and MCP. Best when your agent works on files or a codebase.
- Claude Managed Agents (beta): Anthropic hosts the loop and a sandbox; you configure agents and sessions through the API. Best for long-running background tasks without your own infrastructure.
The Python package is claude-agent-sdk, imported as claude_agent_sdk. It is asynchronous: functions marked async def are started with asyncio.run(...), and async for loops over messages as they arrive while the agent works.
query(prompt=..., options=ClaudeAgentOptions(...)) starts a one-off task. Important options: allowed_tools (tools that run without asking), permission_mode (for example default, acceptEdits or plan), cwd (the folder it works in), max_turns and system_prompt. You get AssistantMessage objects while it works and a final ResultMessage with the result and cost. For a back-and-forth chat, ClaudeSDKClient keeps one session open.
Treat an agent like a new colleague with shell access: give the narrowest tools and folder that do the job.
Example
# pip install claude-agent-sdk (uses ANTHROPIC_API_KEY)
import asyncio
from claude_agent_sdk import (query, ClaudeAgentOptions,
AssistantMessage, ResultMessage, TextBlock)
async def main():
options = ClaudeAgentOptions(
system_prompt="You are a careful code reviewer.",
allowed_tools=["Read", "Grep", "Glob"], # read-only tools run freely
permission_mode="default",
cwd="./my_project",
max_turns=10,
)
async for msg in query(prompt="Find likely bugs in utils.py", options=options):
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(msg, ResultMessage):
print("done:", msg.subtype, "cost $", msg.total_cost_usd)
asyncio.run(main())Remember
- Messages API = full control; Agent SDK = Claude Code's loop and tools in your app; Managed Agents = hosted
query()with ClaudeAgentOptions for one-off tasks; ClaudeSDKClient for ongoing sessions- Restrict allowed_tools, permission_mode and cwd
Watch out for
- Giving an agent broad shell and write access with automatic approval on your real machine.