Skip to content
Free Tool Arena

AI & LLMs · Guide · AI & Prompt Tools

How to Build an Agent with the Claude Agent SDK

Install the Claude Agent SDK, write custom tools, add hooks, and compose sub-agents — the same harness that powers Claude Code.

Updated April 2026 · 6 min read

The Claude Agent SDK is Anthropic’s library for building your own agents on top of Claude Opus 4.7 and Sonnet. It ships the same machinery that powers Claude Code — tools, hooks, MCP, and sub-agents — but as primitives you can wire into your own Python or Node service.

This guide covers the four concepts you need (tools, hooks, MCP servers, sub-agents), installs the current version as of March 2026, and ships a working agent. If you’re deciding between this and the OpenAI Agents SDK, read our agent setup overview first.

Advertisement

Prerequisites

  • Python 3.10+ or Node.js 18+.
  • An Anthropic API key (ANTHROPIC_API_KEY).
  • A spend cap on the console. Agents that loop are expensive; budget first, code second.

Step 1 — Install

Python (version 0.1.48 on PyPI as of March 2026):

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

TypeScript (version 0.2.71 on npm):

npm install @anthropic-ai/agent-sdk

Step 2 — The four core concepts

  • Tools — functions the agent can call. Built-ins cover files, shell, HTTP, and web search. You add your own as needed.
  • Hooks — callbacks that fire before/after tool calls and model turns. Use them for logging, validation, and spend caps.
  • MCP servers — external tool providers the agent can connect to. Slack, GitHub, databases, your internal APIs.
  • Sub-agents — narrower specialists the main agent delegates to. Smaller prompts, better results, easier debugging.

Step 3 — Minimum working agent (Python)

from claude_agent_sdk import query, AssistantMessage, TextBlock
import asyncio

async def main():
    async for message in query(prompt="Explain MCP in 2 sentences."):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text)

asyncio.run(main())

That’s a working agent. No tools yet, no guardrails — just Claude, reachable through the SDK. Run it to confirm your key works before you layer anything on.

Step 4 — Add a custom tool

Custom tools in the Claude Agent SDK are in-process MCP servers. That sounds heavy; it isn’t. Wrap a Python function with @tool and register it via create_sdk_mcp_server.

from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, query

@tool("square", "Square a number", {"n": int})
async def square(args):
    return {"content": [{"type": "text", "text": str(args["n"] ** 2)}]}

math_server = create_sdk_mcp_server(name="math", tools=[square])

opts = ClaudeAgentOptions(
    mcp_servers={"math": math_server},
    allowed_tools=["mcp__math__square"],
)

async def run():
    async for m in query(prompt="Square 17 using your tool.", options=opts):
        print(m)

Two things matter here. First, custom tools are MCP tools with a mcp__<server>__<tool> namespace — that’s why the allow-list string looks the way it does. Second, allowed_tools is a safety fence: the agent cannot call anything not on that list.

Step 5 — Add a hook

Hooks let you veto, log, or modify the agent’s behaviour without changing the prompt.

async def pre_tool(tool_name, args, ctx):
    print(f"-> calling {tool_name} with {args}")
    return {"allow": True}

opts = ClaudeAgentOptions(
    mcp_servers={"math": math_server},
    allowed_tools=["mcp__math__square"],
    hooks={"pre_tool_use": [pre_tool]},
)

Flip allow to False to block the call. This is where you put your spend cap (“if total_tokens > 100k: block”) or your domain allow-list for HTTP fetches.

Step 6 — Connect an external MCP server

Say you want the agent to read Slack. Install the Slack MCP server (one line in your agent options), allow-list the specific tools you want, and the agent can use them. See our dedicated MCP setup guide for the full pattern.

Step 7 — Sub-agents for complex jobs

When one agent’s tool list grows past ~8 tools, or its instructions pass ~300 lines, break off a sub-agent. The main agent invokes the sub-agent as if it were a tool; the sub-agent has its own allow-list, its own instructions, and its own guardrails. Sub-agents are the cleanest way to keep a production agent reliable as scope grows.

Step 8 — Deploy

Any host that runs Python or Node runs this. For async workloads, the SDK works cleanly inside FastAPI, Cloudflare Workers (TS), or AWS Lambda. Add a request-level timeout, a token cap, and structured logging — and you have a production agent. For dev-tool agents specifically, see our Claude Code setup guide — Claude Code is built on top of this same SDK.

Common mistakes

  • Forgetting allowed_tools. Without it the agent has no tool permissions and silently fails to call anything.
  • Giving the agent a single 40-tool server instead of several small ones. Tool choice accuracy drops fast over ~12 options.
  • Not running prompts through our token counter first. Agent turns are priced by input plus output; a chatty system prompt costs you every loop.