plugins/ai-tooling/skills/claude-agent-sdk/SKILL.md
Build apps with the Claude Agent SDK (formerly Claude Code SDK). Covers programmatic agent loops, tool integration, subagent orchestration, prompt caching, and migration between Claude model versions. TRIGGER WHEN: code references claude-agent-sdk, user says "agent sdk", "build an agent", "programmatic claude", "claude code sdk", "sidecar", "run claude programmatically". DO NOT TRIGGER WHEN: user is using the Claude API client SDK (`anthropic`/`@anthropic-ai/sdk`) for direct chat completions, or doing general programming unrelated to agent orchestration.
npx skillsauth add acaprino/alfio-claude-plugins claude-agent-sdkInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Claude Agent SDK lets you run Claude Code programmatically -- build AI agents that read files, write code, execute commands, search the web, and orchestrate subagents, all from your application code.
Key distinction: The Agent SDK (claude-agent-sdk) runs the full Claude Code agent loop with built-in tools. The Anthropic Client SDK (anthropic) is for raw API calls. Use the Agent SDK when you need autonomous tool-using agents.
| | TypeScript | Python |
|---|---|---|
| Package | @anthropic-ai/claude-agent-sdk | claude-agent-sdk |
| Install | npm install @anthropic-ai/claude-agent-sdk | pip install claude-agent-sdk |
| Auth | ANTHROPIC_API_KEY env var | ANTHROPIC_API_KEY env var |
| Core function | query() | query() |
| GitHub | anthropics/claude-agent-sdk-typescript | anthropics/claude-agent-sdk-python |
The CLI package @anthropic-ai/claude-code is bundled inside the SDK -- no separate install needed.
# TypeScript
npm install @anthropic-ai/claude-agent-sdk
# Python
pip install claude-agent-sdk
# or with uv
uv add claude-agent-sdk
Authentication via environment variable:
export ANTHROPIC_API_KEY=sk-ant-...
Alternative providers:
CLAUDE_CODE_USE_BEDROCK=1 + AWS credentialsCLAUDE_CODE_USE_VERTEX=1 + GCP credentialsCLAUDE_CODE_USE_FOUNDRY=1 + Azure credentialsquery()Both SDKs expose query() as the primary entry point. It returns an async iterator streaming SDKMessage objects. Claude handles the entire tool loop autonomously -- you do NOT implement tool execution.
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", "Bash"],
maxTurns: 10,
},
})) {
if (message.type === "assistant" && message.content) {
for (const block of message.content) {
if (block.type === "text") process.stdout.write(block.text);
}
}
if ("result" in message) {
console.log("\nFinal:", message.result);
console.log("Cost:", message.total_cost_usd);
}
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Bash"],
max_turns=10,
)
async for message in query(prompt="Find and fix the bug in auth.py", options=options):
if hasattr(message, "result"):
print(f"Final: {message.result}")
print(f"Cost: ${message.total_cost_usd:.4f}")
asyncio.run(main())
| Option (TS / Py) | Type | Description |
|---|---|---|
| allowedTools / allowed_tools | string[] | Tools to auto-approve without user confirmation |
| disallowedTools / disallowed_tools | string[] | Tools to always deny |
| permissionMode / permission_mode | string | Permission strategy (see Permissions section) |
| systemPrompt / system_prompt | string | Custom system prompt or "claude_code" for default |
| model | string | Model ID (e.g., "claude-sonnet-4-7", "claude-opus-4-7", "claude-haiku-4-5") -- short aliases resolve to the latest date-slugged release (e.g., "claude-sonnet-4-5-20250929"); pin a full slug for reproducibility |
| maxTurns / max_turns | number | Maximum agentic loop iterations |
| maxBudgetUsd / max_budget_usd | number | Spending cap in USD |
| effort | string | "low", "medium", "high", "max" |
| cwd | string | Working directory for file operations |
| mcpServers / mcp_servers | object | MCP server configurations |
| hooks | object | Lifecycle hook callbacks |
| agents | object | Subagent definitions |
| resume | string | Session ID to resume |
| continue / continue_conversation | boolean | Continue most recent session |
| forkSession / fork_session | string | Fork from an existing session |
| settingSources / setting_sources | string[] | Load settings from ["user", "project", "local"] |
| plugins | string[] | Local plugin directory paths |
| sandbox | object | Sandbox/isolation settings |
| thinking | object | Extended thinking: "adaptive", {type: "enabled", budget: N}, "disabled" |
| outputFormat / output_format | object | JSON schema for structured output |
| env | object | Environment variables passed to agent |
| canUseTool / can_use_tool | function | Runtime permission callback |
| includePartialMessages / include_partial_messages | boolean | Enable token-level streaming |
| spawnClaudeCodeProcess | function | Custom process spawner (VMs, containers, remote) |
| agentProgressSummaries | boolean | Enable periodic AI-generated progress summaries for running subagents |
| debug / debug | boolean | Enable programmatic debug logging |
| debugFile / debug_file | string | File path for debug log output |
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const msg of query({
prompt: "Refactor the auth module to use JWT tokens",
options: {
model: "claude-sonnet-4-7",
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
disallowedTools: ["WebSearch", "WebFetch"],
permissionMode: "bypassPermissions",
maxTurns: 25,
maxBudgetUsd: 1.0,
effort: "high",
cwd: "/home/user/project",
systemPrompt: "You are a senior backend engineer. Follow the project's coding standards.",
thinking: "adaptive",
env: { NODE_ENV: "development" },
},
})) {
// process messages
}
The agent has access to these tools by default:
| Tool | Purpose |
|---|---|
| Read | Read files from filesystem |
| Write | Create new files |
| Edit | Precise string replacements in existing files |
| Bash | Execute shell commands |
| Glob | Find files by pattern |
| Grep | Search file contents with regex |
| WebSearch | Search the web |
| WebFetch | Fetch and parse web pages |
| Agent | Spawn subagents (required for multi-agent) |
| Skill | Invoke skills from plugins |
| AskUserQuestion | Request user input |
| TodoWrite | Manage task lists |
| ToolSearch | Discover deferred tools |
Control which tools the agent can use:
// Only allow read-only operations
options: {
allowedTools: ["Read", "Glob", "Grep"],
disallowedTools: ["Bash", "Write", "Edit"],
}
Create custom tools using the SDK's MCP server helpers. Tools are defined with schemas and handlers, then exposed as in-process MCP servers.
import { tool, createSdkMcpServer, query } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
// Define tools
const getWeather = tool(
"get_weather",
"Get current weather for a city",
{ city: z.string(), units: z.enum(["celsius", "fahrenheit"]).default("celsius") },
async ({ city, units }) => ({
content: [{ type: "text", text: JSON.stringify({ city, temp: 22, units }) }],
})
);
const searchDatabase = tool(
"search_db",
"Search the application database",
{ query: z.string(), limit: z.number().default(10) },
async ({ query: q, limit }) => {
const results = await db.search(q, limit);
return { content: [{ type: "text", text: JSON.stringify(results) }] };
}
);
// Create MCP server
const server = createSdkMcpServer({
name: "app-tools",
tools: [getWeather, searchDatabase],
});
// Use in query
for await (const msg of query({
prompt: "What's the weather in Rome and find related travel posts?",
options: {
mcpServers: { app: server },
allowedTools: ["mcp__app__get_weather", "mcp__app__search_db"],
},
})) {
// Custom tools are called automatically by the agent
}
from claude_agent_sdk import tool, create_sdk_mcp_server, query, ClaudeAgentOptions
@tool("get_weather", "Get current weather for a city", {"city": str, "units": str})
async def get_weather(args):
return {"content": [{"type": "text", "text": f"Weather in {args['city']}: 22C"}]}
@tool("search_db", "Search the database", {"query": str, "limit": int})
async def search_db(args):
results = await db.search(args["query"], args["limit"])
return {"content": [{"type": "text", "text": str(results)}]}
server = create_sdk_mcp_server(name="app-tools", tools=[get_weather, search_db])
options = ClaudeAgentOptions(
mcp_servers={"app": server},
allowed_tools=["mcp__app__get_weather", "mcp__app__search_db"],
)
async for msg in query(prompt="Weather in Rome?", options=options):
pass
Custom tools follow the pattern: mcp__<server-name>__<tool-name>
Example: server named "mytools" with tool "search" becomes mcp__mytools__search
Connect to external MCP servers via stdio or HTTP:
options: {
mcpServers: {
// stdio transport (local process)
localServer: {
command: "node",
args: ["./mcp-server.js"],
},
// HTTP/SSE transport (remote)
remoteServer: {
url: "https://mcp.example.com/sse",
headers: { Authorization: "Bearer token" },
},
},
}
Subagents let you define specialized agents that the main agent can spawn for parallel or delegated work.
for await (const msg of query({
prompt: "Review the codebase for quality and security issues",
options: {
allowedTools: ["Read", "Grep", "Glob", "Agent"], // Agent tool required
agents: {
"security-reviewer": {
description: "Security expert. Use for vulnerability scanning, auth review, injection detection.",
prompt: "You are a security auditor. Find vulnerabilities, auth issues, and injection vectors.",
tools: ["Read", "Glob", "Grep"],
model: "opus",
},
"code-quality": {
description: "Code quality reviewer. Use for style, patterns, complexity, dead code.",
prompt: "Review code quality: naming, complexity, patterns, duplication.",
tools: ["Read", "Glob", "Grep"],
model: "sonnet",
},
"test-runner": {
description: "Runs tests and reports results.",
prompt: "Execute test suites and analyze failures.",
tools: ["Bash", "Read", "Grep"],
model: "haiku",
},
},
},
})) {
// Claude automatically decides when to spawn subagents
// based on their descriptions
}
| Property | Type | Description |
|---|---|---|
| description | string | Required. When to use this agent (Claude decides based on this) |
| prompt | string | Required. System prompt for the subagent |
| tools | string[] | Restricted tool set |
| model | string | "sonnet", "opus", "haiku", or "inherit" |
| disallowedTools | string[] | Tools to block (TS only) |
| mcpServers | object | MCP servers available to subagent |
| skills | string[] | Skills the subagent can invoke |
| memory | object | Memory configuration for the subagent |
| maxTurns | number | Turn limit for this subagent (TS only) |
agentProgressSummaries: true to receive periodic AI-generated progress updates from running subagentsSessions persist conversation history to disk, enabling multi-turn workflows.
let sessionId: string | undefined;
// First query -- capture session ID
for await (const msg of query({
prompt: "Read the authentication module",
options: { allowedTools: ["Read", "Glob"] },
})) {
if (msg.type === "system" && msg.subtype === "init") {
sessionId = msg.session_id;
}
}
// Second query -- resume with full context
for await (const msg of query({
prompt: "Now refactor it to use JWT",
options: { resume: sessionId },
})) {
if ("result" in msg) console.log(msg.result);
}
// TypeScript
for await (const msg of query({
prompt: "Continue where we left off",
options: { continue: true },
})) { /* ... */ }
# Python
options = ClaudeAgentOptions(continue_conversation=True)
async for msg in query(prompt="Continue where we left off", options=options):
pass
Python provides ClaudeSDKClient for managed multi-turn conversations:
from claude_agent_sdk import ClaudeSDKClient
async with ClaudeSDKClient() as client:
# First turn
await client.query("What's the project structure?")
async for msg in client.receive_response():
pass # process
# Second turn -- context retained automatically
await client.query("Now find all API endpoints")
async for msg in client.receive_response():
pass # process
# Interrupt current generation
await client.interrupt()
import {
listSessions, getSessionInfo, getSessionMessages,
forkSession, tagSession, renameSession,
} from "@anthropic-ai/claude-agent-sdk";
// List sessions
const sessions = await listSessions({ dir: "/path/to/project", limit: 10 });
for (const session of sessions) {
console.log(session.sessionId, session.createdAt, session.tag);
}
// Single-session metadata lookup
const info = await getSessionInfo(sessionId);
console.log(info.tag, info.createdAt);
// Read conversation history (includes parallel tool results)
const messages = await getSessionMessages(sessionId);
// Branch a conversation from a specific point
const forked = await forkSession(sessionId);
// Organize sessions with tags and renames
await tagSession(sessionId, "auth-refactor");
await renameSession(sessionId, "auth-refactor-v2");
from claude_agent_sdk import (
list_sessions, get_session_info, get_session_messages,
fork_session, tag_session, rename_session,
)
sessions = await list_sessions(dir="/path/to/project", limit=10)
for session in sessions:
messages = await get_session_messages(session.session_id)
info = await get_session_info(session_id)
await tag_session(session_id, "auth-refactor")
await rename_session(session_id, "auth-refactor-v2")
Session state change events are opt-in as of v0.2.83. Enable with environment variable:
export CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS=1
The ExitReason type includes: "end_turn", "max_turns", "budget", "interrupt", "resume".
import { supportedAgents, getSettings } from "@anthropic-ai/claude-agent-sdk";
// Discover available subagents
const agents = await supportedAgents();
// Inspect runtime-resolved settings (includes applied model and effort)
const settings = await getSettings();
console.log(settings.applied.model, settings.applied.effort);
Control what the agent can do at runtime.
| Mode | Behavior |
|---|---|
| "default" | Unmatched tools trigger canUseTool callback or user prompt |
| "dontAsk" | Deny anything not pre-approved (TypeScript only) |
| "acceptEdits" | Auto-accept file mutations (Edit, Write, mkdir, touch, rm, mv, cp) |
| "bypassPermissions" | All tools run without prompts (use with caution) |
| "plan" | No execution -- planning/analysis only |
PreToolUse)disallowedTools) -- overrides everything, including bypassPermissionsallowedTools) -- does NOT constrain bypassPermissionscanUseTool callbackImportant: disallowedTools is the only hard block. Even bypassPermissions cannot override it. Use it for safety-critical restrictions.
for await (const msg of query({
prompt: "Deploy the application",
options: {
allowedTools: ["Read", "Bash"],
canUseTool: async (toolName, input) => {
// Block destructive commands
if (toolName === "Bash" && input.command?.includes("rm -rf")) {
return { behavior: "deny", message: "Destructive commands not allowed" };
}
// Allow everything else
return { behavior: "allow" };
},
},
})) { /* ... */ }
Callback return values:
{ behavior: "allow" } -- approve the tool call{ behavior: "deny", message: "reason" } -- reject with explanation{ behavior: "ask" } -- fall through to user prompt (default mode)Hooks intercept agent lifecycle events for logging, validation, or control flow.
| Hook | When | Can modify? |
|---|---|---|
| PreToolUse | Before a tool executes | Yes -- allow, deny, or modify input |
| PostToolUse | After a tool completes | Yes -- modify output |
| PostToolUseFailure | After a tool fails | Log errors |
| UserPromptSubmit | When user sends a prompt | Modify prompt text |
| Stop | Agent is about to stop | Force continue or modify |
| SubagentStart | Subagent is starting | Modify subagent config |
| SubagentStop | Subagent completed | Process results |
| PreCompact | Before context compaction | Log or modify |
| Notification | Agent sends a notification | Display or forward |
| PermissionRequest | Tool needs permission | Auto-approve or deny |
| TaskCompleted | A task has been completed | Process results |
| TeammateIdle | A teammate agent is idle | Reassign or notify |
| ConfigChange | Configuration changed at runtime | Security auditing |
| SessionStart (TS) | Session initialized | Setup actions |
| SessionEnd (TS) | Session completed | Cleanup actions |
Hooks can use regex matchers to target specific tools, and support async (fire-and-forget) mode:
hooks: {
PreToolUse: [
// Matcher targets specific tools by regex on tool name
{
matcher: /^(Write|Edit)$/,
hooks: [async (event) => {
if (event.input.file_path?.includes(".env")) {
return { behavior: "deny", message: "Cannot modify .env files" };
}
return { behavior: "allow" };
}],
timeout: 30, // seconds, default 60
},
// Async hook -- fire-and-forget logging (does not block)
{
matcher: /.*/,
hooks: [{ async: true, asyncTimeout: 5, handler: async (event) => {
await fetch("https://logs.example.com/webhook", {
method: "POST",
body: JSON.stringify({ tool: event.toolName, time: Date.now() }),
});
}}],
},
],
}
Hook outputs can include:
behavior: "allow", "deny", or "ask" (deny > ask > allow when multiple hooks conflict)systemMessage: inject a system message into conversationcontinue: force the agent to continue (for Stop hooks)hookSpecificOutput: event-specific modificationsfor await (const msg of query({
prompt: "Analyze the codebase",
options: {
hooks: {
PreToolUse: async (event) => {
console.log(`Tool: ${event.toolName}, Input: ${JSON.stringify(event.input)}`);
// Block writes to production config
if (event.toolName === "Write" && event.input.file_path?.includes("production")) {
return { behavior: "deny", message: "Cannot modify production files" };
}
return { behavior: "allow" };
},
PostToolUse: async (event) => {
console.log(`Tool ${event.toolName} completed in ${event.duration_ms}ms`);
},
Stop: async (event) => {
console.log(`Agent stopping. Result: ${event.result}`);
},
},
},
})) { /* ... */ }
Messages streamed from query() include:
| Type | Description |
|---|---|
| system (subtype: init) | Session initialized -- contains session_id |
| system (subtype: api_retry) | API retry info -- attempt count, max retries, delay, error status |
| assistant | Claude's response with content blocks (text, tool_use) |
| result | Final result with result text, total_cost_usd, usage |
| task_progress | Real-time usage metrics for running agents |
| rate_limit | Rate limit event with retry timing (Python: RateLimitEvent) |
| stream_event | Partial token (when includePartialMessages: true) |
for await (const msg of query({
prompt: "Explain the auth flow",
options: {
includePartialMessages: true,
allowedTools: ["Read"],
},
})) {
if (msg.type === "stream_event") {
process.stdout.write(msg.delta?.text ?? "");
}
}
Force the agent to return JSON conforming to a schema:
for await (const msg of query({
prompt: "Analyze this codebase and list all API endpoints",
options: {
outputFormat: {
type: "json_schema",
json_schema: {
name: "api_analysis",
schema: {
type: "object",
properties: {
endpoints: {
type: "array",
items: {
type: "object",
properties: {
method: { type: "string" },
path: { type: "string" },
handler: { type: "string" },
},
required: ["method", "path", "handler"],
},
},
},
required: ["endpoints"],
},
},
},
},
})) {
if ("result" in msg) {
const analysis = JSON.parse(msg.result);
console.log(analysis.endpoints);
}
}
Load local plugins to give the agent access to custom skills, agents, and commands:
for await (const msg of query({
prompt: "Review the frontend code",
options: {
plugins: ["/path/to/my-plugin"],
allowedTools: ["Read", "Glob", "Grep", "Skill"],
},
})) { /* agent can now invoke skills from the plugin */ }
Load filesystem settings (CLAUDE.md, .claude/ configs):
options: {
settingSources: ["user", "project", "local"],
}
for await (const msg of query({ prompt: "Analyze auth.py", options: {} })) {
if ("result" in msg) {
console.log(`Total cost: $${msg.total_cost_usd}`);
console.log(`Input tokens: ${msg.usage?.input_tokens}`);
console.log(`Output tokens: ${msg.usage?.output_tokens}`);
// Per-model breakdown (TypeScript)
if (msg.modelUsage) {
for (const [model, usage] of Object.entries(msg.modelUsage)) {
console.log(`${model}: $${usage.cost_usd}`);
}
}
}
}
Use maxBudgetUsd to set a hard spending cap:
options: { maxBudgetUsd: 0.50 } // stop after $0.50
New container per task, destroy on completion. Best for CI/CD, one-shot tasks.
// In a serverless function or CI step
const result = [];
for await (const msg of query({
prompt: taskDescription,
options: {
maxTurns: 15,
maxBudgetUsd: 0.25,
permissionMode: "bypassPermissions",
cwd: "/workspace",
},
})) {
if ("result" in msg) result.push(msg);
}
Persistent containers with multiple agent interactions. Best for interactive applications.
// Resume across container restarts
const sessionId = await loadSessionId();
for await (const msg of query({
prompt: userInput,
options: { resume: sessionId },
})) { /* ... */ }
Run agents in VMs, containers, or remote environments:
for await (const msg of query({
prompt: "Analyze the repo",
options: {
spawnClaudeCodeProcess: async (options) => {
// Spawn in a Docker container, VM, or remote server
const container = await docker.createContainer({
Image: "node:20",
Cmd: ["npx", "@anthropic-ai/claude-code", ...options.args],
});
await container.start();
return container.stream;
},
},
})) { /* ... */ }
Use Anthropic's sandbox runtime for secure execution:
import { createSandbox } from "@anthropic-ai/sandbox-runtime";
const sandbox = await createSandbox({ image: "node:20" });
for await (const msg of query({
prompt: "Run the test suite",
options: { sandbox },
})) { /* ... */ }
allowedTools -- restrict to minimum necessary toolsmaxBudgetUsd -- prevent runaway costsmaxTurns -- prevent infinite loopscanUseTool callbacks -- validate dangerous operations at runtimeenv option or MCP tools for credential accessdisallowedTools -- explicitly block tools you never want used// Secure configuration example
options: {
allowedTools: ["Read", "Glob", "Grep"], // read-only
disallowedTools: ["Bash", "Write", "Edit"], // no execution or mutation
maxTurns: 10,
maxBudgetUsd: 0.25,
permissionMode: "dontAsk", // deny anything not listed
canUseTool: async (tool, input) => {
// Additional runtime validation
if (input.file_path?.includes("..")) {
return { behavior: "deny", message: "Path traversal blocked" };
}
return { behavior: "allow" };
},
}
import { query } from "@anthropic-ai/claude-agent-sdk";
async function reviewPR(diff: string): Promise<string> {
let review = "";
for await (const msg of query({
prompt: `Review this PR diff for bugs, security issues, and style:\n\n${diff}`,
options: {
allowedTools: ["Read", "Glob", "Grep"],
maxTurns: 15,
maxBudgetUsd: 0.50,
outputFormat: {
type: "json_schema",
json_schema: {
name: "pr_review",
schema: {
type: "object",
properties: {
issues: { type: "array", items: { type: "object", properties: {
severity: { type: "string" }, file: { type: "string" },
line: { type: "number" }, description: { type: "string" },
}}},
summary: { type: "string" },
approved: { type: "boolean" },
},
required: ["issues", "summary", "approved"],
},
},
},
},
})) {
if ("result" in msg) review = msg.result;
}
return review;
}
for await (const msg of query({
prompt: "Research best practices for rate limiting in Node.js APIs",
options: {
allowedTools: ["Read", "Grep", "Glob", "WebSearch", "WebFetch", "Agent"],
agents: {
"codebase-analyst": {
description: "Analyzes the local codebase for existing patterns.",
prompt: "Search the codebase for rate limiting implementations and patterns.",
tools: ["Read", "Glob", "Grep"],
model: "sonnet",
},
"web-researcher": {
description: "Researches best practices and documentation online.",
prompt: "Search for current best practices, libraries, and patterns.",
tools: ["WebSearch", "WebFetch"],
model: "sonnet",
},
},
},
})) { /* ... */ }
from claude_agent_sdk import ClaudeSDKClient
async def chat_loop():
async with ClaudeSDKClient() as client:
while True:
user_input = input("You: ")
if user_input.lower() == "exit":
break
await client.query(user_input)
async for msg in client.receive_response():
if hasattr(msg, "content"):
for block in msg.content:
if block.get("type") == "text":
print(f"Agent: {block['text']}")
The old claude-code-sdk / @anthropic-ai/claude-code-sdk packages are deprecated. Migration:
# TypeScript
npm uninstall @anthropic-ai/claude-code-sdk
npm install @anthropic-ai/claude-agent-sdk
# Python
pip uninstall claude-code-sdk
pip install claude-agent-sdk
Update imports:
// Old
import { query } from "@anthropic-ai/claude-code-sdk";
// New
import { query } from "@anthropic-ai/claude-agent-sdk";
# Old
from claude_code_sdk import query, ClaudeCodeOptions
# New
from claude_agent_sdk import query, ClaudeAgentOptions
The API surface is mostly identical, but two critical defaults changed:
System prompt no longer defaults to Claude Code's prompt -- the new SDK uses a minimal system prompt. To restore Claude Code behavior:
systemPrompt: { type: "preset", preset: "claude_code" }
Settings sources no longer loaded by default -- CLAUDE.md, .claude/ configs, and user settings are NOT loaded unless you opt in:
settingSources: ["user", "project", "local"]
To fully restore old claude-code-sdk behavior, use both options together.
A simplified session-based API is available as a preview:
import {
unstable_v2_createSession,
unstable_v2_resumeSession,
unstable_v2_prompt,
} from "@anthropic-ai/claude-agent-sdk";
// Create a session
await using session = await unstable_v2_createSession({
model: "claude-sonnet-4-6",
allowedTools: ["Read", "Edit", "Bash"],
});
// Send a prompt and stream response
const response = session.send("Find bugs in auth.py");
for await (const event of response) {
// process streaming events
}
// Or use stream() for full message objects
const stream = session.stream("Now fix them");
for await (const msg of stream) {
// process messages
}
// Resume a previous session
const resumed = await unstable_v2_resumeSession(sessionId);
// One-shot helper (no session management)
for await (const msg of unstable_v2_prompt("Quick question", { model: "claude-haiku-4-5" })) {
// process
}
await using (TypeScript 5.2+) automatically closes the session when the scope exits. The V2 API does not yet support session forking.
The Python ClaudeSDKClient provides additional runtime control methods:
async with ClaudeSDKClient() as client:
# Core conversation
await client.query("Analyze the codebase")
async for msg in client.receive_response():
pass
# Runtime controls
await client.set_permission_mode("bypassPermissions")
await client.set_model("claude-sonnet-4-6")
# File management
await client.rewind_files() # undo file changes made by agent
# MCP server management
status = await client.get_mcp_status()
await client.add_mcp_server("new-server", {"command": "node", "args": ["server.js"]})
await client.remove_mcp_server("old-server")
info = await client.get_server_info()
# Interrupt current generation
await client.interrupt()
# Clean disconnect
await client.disconnect()
development
Quality gates for multi-reviewer code review pipelines: adversarial verification panel, completeness critic, reviewer pipeline conventions, and the context sharing pattern for parallel reviewers. TRIGGER WHEN: running /senior-review:team-review quality gates; running /senior-review:code-review Steps 4b/4c (adversarial verification and completeness check); consolidating or deduplicating findings from multiple parallel reviewers. DO NOT TRIGGER WHEN: single-reviewer style review without a consolidation phase, or generic team coordination (the upstream agent-teams skills cover that).
development
Knowledge base for pure-architecture decisions on when to unify duplicated logic into a shared abstraction versus leave it duplicated. Covers the canonical theory (Rule of Three, DRY/WET/AHA, Wrong Abstraction, Locality of Behaviour, Bounded Contexts, Tidy First options framing, CUPID vs SOLID), 12 essential-duplication patterns that justify unification, 12 wrong-abstraction patterns that justify inlining or decomposition, an operational decision frame, and a verified reading list. TRIGGER WHEN: the user is making an architectural decision about whether to centralize, extract, or remove a layer; reviewing an abstraction for premature generality; auditing scattered cross-cutting concerns; spawned by the abstraction-architect agent during /abstraction-architect:audit or as the Abstraction dimension of /senior-review:team-review or /senior-review:code-review; the user asks "should I extract this into a service" / "is this DRY enough" / "is this wrong abstraction". DO NOT TRIGGER WHEN: the task is code formatting and readability cleanup (use clean-code:clean-code), Python-specific refactoring with metrics (use python-development:python-refactor), generic dead-code removal (use senior-review:cleanup-dead-code), security review (use senior-review:security-auditor), or pure pattern-consistency review without an architecture lens (use senior-review:code-auditor).
development
Unified web frontend knowledge base covering CSS architecture, UX psychology, UI components, distinctive aesthetics, and interface design generation. TRIGGER WHEN: working on web styling, design systems, component decisions, responsive strategy, distinctive frontend aesthetics, or exploring multiple interface designs. DO NOT TRIGGER WHEN: the task is purely backend or unrelated to web frontend.
development
Stripe payments knowledge base - API patterns, checkout optimization, subscription lifecycle, pricing strategies, webhook reliability, Firebase integration, cost analysis, and revenue modeling. Loaded by stripe-integrator and revenue-optimizer agents; also consumable directly when the user asks for Stripe-specific patterns without needing an agent. TRIGGER WHEN: working with Stripe API (Payment Intents, Customers, Subscriptions, Checkout Sessions, Connect, webhooks, tax, usage-based billing), pricing strategy, or revenue modeling. DO NOT TRIGGER WHEN: payment work is non-Stripe (PayPal, Square, crypto) or the task is generic e-commerce unrelated to payments.