claude-agent-sdk

Provides patterns and best practices for building production AI agents using the Claude Agent SDK (Python: claude-agent-sdk , TypeScript: @anthropic-ai/claude-agent-sdk ).

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "claude-agent-sdk" with this command: npx skills add the-perfect-developer/the-perfect-opencode/the-perfect-developer-the-perfect-opencode-claude-agent-sdk

Claude Agent SDK

Provides patterns and best practices for building production AI agents using the Claude Agent SDK (Python: claude-agent-sdk , TypeScript: @anthropic-ai/claude-agent-sdk ).

Installation

TypeScript

npm install @anthropic-ai/claude-agent-sdk

Python (uv — recommended)

uv add claude-agent-sdk

Python (pip)

pip install claude-agent-sdk

Set the API key before running any agent:

export ANTHROPIC_API_KEY=your-api-key

Third-party providers are also supported: set CLAUDE_CODE_USE_BEDROCK=1 , CLAUDE_CODE_USE_VERTEX=1 , or CLAUDE_CODE_USE_FOUNDRY=1 alongside the respective cloud credentials.

Core Pattern: The query() Function

Every agent is built around query() , which returns an async iterator of streamed messages:

import asyncio from claude_agent_sdk import query, ClaudeAgentOptions

async def main(): async for message in query( prompt="Find and fix the bug in auth.py", options=ClaudeAgentOptions( allowed_tools=["Read", "Edit", "Glob"], permission_mode="acceptEdits", ), ): if hasattr(message, "result"): print(message.result)

asyncio.run(main())

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

for await (const message of query({ prompt: "Find and fix the bug in auth.py", options: { allowedTools: ["Read", "Edit", "Glob"], permissionMode: "acceptEdits" } })) { if ("result" in message) console.log(message.result); }

The loop ends when Claude finishes or hits an error. The SDK handles tool execution, context management, and retries internally.

Built-in Tools

Grant only the tools the agent actually needs — principle of least privilege:

Tool Purpose

Read

Read any file in the working directory

Write

Create new files

Edit

Make precise edits to existing files

Bash

Run terminal commands, scripts, git operations

Glob

Find files by pattern (/*.ts , src//*.py )

Grep

Search file contents with regex

WebSearch

Search the web for current information

WebFetch

Fetch and parse web page content

AskUserQuestion

Ask the user clarifying questions

Task

Spawn subagents (required when using subagents)

Recommended tool sets by use case

Use case Tools

Read-only analysis Read , Glob , Grep

Code modification Read , Edit , Write , Glob , Grep

Full automation Read , Edit , Write , Bash , Glob , Grep

Web-augmented Add WebSearch , WebFetch to any set

Subagent orchestration Add Task to the parent agent's set

Permission Modes

Set permissionMode / permission_mode in options:

Mode Behavior Best for

default

Delegates unresolved requests to canUseTool callback Custom approval flows

acceptEdits

Auto-approves file edits and filesystem ops Trusted dev workflows

bypassPermissions

Runs all tools without prompts CI/CD pipelines

plan

No tool execution — planning only Pre-review before changes

Best practice: use acceptEdits for interactive development; use bypassPermissions only in isolated, sandboxed environments. Never use bypassPermissions in multi-tenant or user-facing applications.

Permission mode changes mid-session are supported — start restrictive, loosen after reviewing Claude's plan:

q = query(prompt="Refactor auth module", options=ClaudeAgentOptions(permission_mode="plan")) await q.set_permission_mode("acceptEdits") # Switch after plan is approved async for message in q: ...

Permission evaluation order

  • Hooks — run first; can allow, deny, or pass through

  • Permission rules — declarative allow/deny in settings.json

  • Permission mode — global fallback setting

  • canUseTool callback — runtime user approval (when mode is default )

Session Management

Capture session ID

session_id = None async for message in query(prompt="Analyze auth module", options=ClaudeAgentOptions(...)): if hasattr(message, "subtype") and message.subtype == "init": session_id = message.data.get("session_id")

let sessionId: string | undefined; for await (const message of query({ prompt: "Analyze auth module", options: { ... } })) { if (message.type === "system" && message.subtype === "init") { sessionId = message.session_id; } }

Resume a session

Pass resume / resume in options to continue with full prior context:

async for message in query( prompt="Now find all callers of that function", options=ClaudeAgentOptions(resume=session_id), ): ...

Fork a session

Set fork_session=True / forkSession: true to branch without modifying the original:

Explore a different approach without losing the original session

async for message in query( prompt="Redesign this as GraphQL instead", options=ClaudeAgentOptions(resume=session_id, fork_session=True), ): ...

Forking preserves the original session; both branches can be resumed independently.

MCP Integration

Connect external services through the Model Context Protocol:

options = ClaudeAgentOptions( mcp_servers={ "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]} } )

MCP tool names follow the pattern mcp__{server_name}__{tool_name} . List them explicitly in allowed_tools to restrict access:

allowed_tools=["mcp__playwright__browser_click", "mcp__playwright__browser_screenshot"]

Message Handling

Filter the stream for meaningful output. Raw messages include system init and internal state:

from claude_agent_sdk import AssistantMessage, ResultMessage

async for message in query(...): if isinstance(message, AssistantMessage): for block in message.content: if hasattr(block, "text"): print(block.text) # Claude's reasoning elif hasattr(block, "name"): print(f"Tool: {block.name}") # Tool being called elif isinstance(message, ResultMessage): print(f"Result: {message.subtype}") # "success" or "error"

for await (const message of query({ ... })) { if (message.type === "assistant") { for (const block of message.message.content) { if ("text" in block) console.log(block.text); else if ("name" in block) console.log(Tool: ${block.name}); } } else if (message.type === "result") { console.log(Result: ${message.subtype}); } }

System Prompts

Provide a system_prompt / systemPrompt to give Claude a persona or project-specific context:

options = ClaudeAgentOptions( system_prompt="You are a senior Python developer. Always follow PEP 8. Prefer explicit error handling over bare excepts.", allowed_tools=["Read", "Edit", "Glob"], permission_mode="acceptEdits", )

Keep system prompts concise and focused on constraints the LLM doesn't already know.

Best Practices

Tool selection

  • Grant only the tools the task requires — no Bash for read-only analysis

  • Verify tool set is sufficient before launching; a missing tool causes the agent to stall

  • Include Task in the parent's allowed_tools when defining subagents

Prompts

  • Write prompts as specific task instructions, not open-ended descriptions

  • Name files explicitly when they are the target ("Review auth.py" vs "Review some code" )

  • Use explicit subagent invocation when precision matters: "Use the code-reviewer agent to..." )

Error handling

  • Always handle ResultMessage with subtype == "error" — never assume success

  • Catch exceptions around the async for loop, not inside it

  • Avoid bare except in hook callbacks; a swallowed exception can silently halt the agent

Security

  • Never use bypassPermissions in production systems with user-supplied prompts

  • Use PreToolUse hooks to block access to sensitive paths (.env , /etc , secrets)

  • Restrict subagent tools — subagents do not inherit parent permissions automatically

Sessions

  • Store session IDs persistently if the workflow spans multiple process runs

  • Fork sessions when exploring alternative approaches to avoid losing a good baseline

  • Subagent transcripts persist separately; clean up via cleanupPeriodDays setting

Performance

  • Prefer streaming (async for ) over collecting all messages; it shows progress and allows early exit

  • Use subagents to parallelise independent tasks (security scan + style check simultaneously)

  • Use plan mode first for expensive or irreversible operations — review before executing

Additional Resources

  • references/hooks.md — Lifecycle hooks: blocking tools, modifying inputs, audit logging

  • references/subagents.md — Defining, invoking, and resuming subagents; parallelisation patterns

  • references/custom-tools.md — Building in-process MCP tools with createSdkMcpServer

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

turso-libsql

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

alpinejs

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

git-hooks

No summary provided by upstream source.

Repository SourceNeeds Review