Claude Almanac
Surfaces

Headless Mode and Agent SDK

Run Claude Code non-interactively from the CLI or programmatically via the Python and TypeScript SDKs.

Claude Code Headless Mode and Agent SDK

Headless mode (now called SDK mode) allows you to run Claude Code non-interactively from the command line or within application code.

Note: The Claude Code SDK has been renamed to the Claude Agent SDK. The Python package changed from claude-code-sdk to claude-agent-sdk, and imports changed from claude_code_sdk to claude_agent_sdk. See the Migration Guide for details.

Overview

Two approaches available:

  • CLI with -p flag: For scripts, CI/CD, and automation
  • Agent SDK packages: For full programmatic control with Python or TypeScript

CLI SDK Mode

Basic Usage

# Simple non-interactive query
claude -p "Find and fix the bug in auth.py"

# With allowed tools (auto-approve operations)
claude -p "Find and fix the bug" --allowedTools "Read,Edit,Bash"

# With structured output
claude -p "Summarize this project" --output-format json

Key CLI Flags

FlagPurposeExample
-p, --printNon-interactive modeclaude -p "your prompt"
--bareSkip auto-discoveryclaude --bare -p "query"
--allowedToolsAuto-approve tools--allowedTools "Read,Edit,Bash"
--output-formatOutput format--output-format json
--input-formatInput format--input-format stream-json
--json-schemaValidate output--json-schema '{...}'
--continue, -cResume most recent sessionclaude -c -p "continue"
--resume, -rResume specific sessionclaude -r "session-name"
--max-turnsLimit agentic turns--max-turns 5
--max-budget-usdSpend limit--max-budget-usd 5.00
--modelSpecify model--model opus
--fallback-modelFallback on overload--fallback-model sonnet
--append-system-promptAdd instructions--append-system-prompt "Use TypeScript"
--system-promptReplace system prompt--system-prompt "Custom prompt"
--permission-modePermission level--permission-mode acceptEdits
--toolsRestrict available tools--tools "Read,Bash"
--no-session-persistenceDon't save session to disk--no-session-persistence

Output Formats

Text (default):

claude -p "Explain this project"

JSON:

claude -p "Summarize" --output-format json
# Returns: {"result": "...", "session_id": "...", "usage": {...}}

Streaming JSON:

claude -p "Analyze logs" --output-format stream-json
# Returns newline-delimited JSON

Advanced CLI Examples

# Structured output with schema validation
claude -p "Extract function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}}}'

# Create commit with auto-approved git commands
claude -p "Look at staged changes and create an appropriate commit" \
  --allowedTools "Bash(git diff:*),Bash(git log:*),Bash(git status:*),Bash(git commit:*)"

# Continue conversation across prompts
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$session_id"

Agent SDK

The Agent SDK gives you the same tools, agent loop, and context management that power Claude Code, but programmable.

CLI vs SDK Comparison

AspectCLI (-p)Agent SDK
Use CaseScripts, CI/CD, one-offProduction agents, custom apps
OutputText, JSON, streamingNative message objects
ControlBasic flagsAdvanced hooks, callbacks
AuthenticationClaude Code configAPI key or cloud auth

What the SDK Provides

  • Built-in tools: Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, AskUserQuestion
  • Hooks: PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd, UserPromptSubmit
  • Subagents: Spawn specialized agents for focused subtasks
  • MCP integration: Connect to databases, browsers, APIs
  • Permissions: Fine-grained control over what agents can do
  • Sessions: Maintain context across multiple exchanges
  • Structured outputs: Validate responses against JSON schemas

Python Example: Simple Agent

Package: pip install claude-agent-sdk (PyPI, SDK docs)

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(
        prompt="Review utils.py for bugs that would cause crashes.",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Glob"],
            permission_mode="acceptEdits"
        )
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if hasattr(block, "text"):
                    print(block.text)

asyncio.run(main())

TypeScript Example: Simple Agent

Package: npm install @anthropic-ai/claude-agent-sdk (npm, SDK docs)

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

for await (const message of query({
  prompt: "Review utils.py for bugs.",
  options: {
    allowedTools: ["Read", "Edit", "Glob"],
    permissionMode: "acceptEdits"
  }
})) {
  if (message.type === "assistant" && message.message?.content) {
    for (const block of message.message.content) {
      if ("text" in block) console.log(block.text);
    }
  }
}

Python: Agent with Subagents

from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

async def main():
    async for message in query(
        prompt="Use the code-reviewer agent to review this codebase",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Glob", "Grep", "Agent"],
            agents={
                "code-reviewer": AgentDefinition(
                    description="Expert code reviewer.",
                    prompt="Analyze code quality and suggest improvements.",
                    tools=["Read", "Glob", "Grep"]
                )
            }
        )
    ):
        if hasattr(message, "result"):
            print(message.result)

Python: Agent with Hooks

from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
from datetime import datetime

async def log_file_change(input_data, tool_use_id, context):
    file_path = input_data.get('tool_input', {}).get('file_path', 'unknown')
    with open('./audit.log', 'a') as f:
        f.write(f"{datetime.now()}: modified {file_path}\n")
    return {}

async def main():
    async for message in query(
        prompt="Refactor utils.py",
        options=ClaudeAgentOptions(
            permission_mode="acceptEdits",
            hooks={
                "PostToolUse": [HookMatcher(matcher="Edit|Write", hooks=[log_file_change])]
            }
        )
    ):
        if hasattr(message, "result"):
            print(message.result)

Python: Sessions and Context Persistence

from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage, ResultMessage

async def main():
    session_id = None

    # First query: capture the session ID
    async for message in query(
        prompt="Read the authentication module",
        options=ClaudeAgentOptions(allowed_tools=["Read", "Glob"])
    ):
        if isinstance(message, SystemMessage) and message.subtype == "init":
            session_id = message.data["session_id"]

    # Resume with full context
    async for message in query(
        prompt="Now find all places that call it",
        options=ClaudeAgentOptions(resume=session_id)
    ):
        if isinstance(message, ResultMessage):
            print(message.result)

CI/CD Integration

See GitHub Actions and CI/CD for comprehensive documentation including:

  • Authentication with API key, Max/Pro subscription (OAuth), Vertex AI, or Bedrock
  • Complete workflow examples (PR review, issue-to-PR, CI failure fixes)
  • Cost management and security best practices

Quick Examples

GitHub Actions (API key):

- uses: anthropics/claude-code-action@v1
  with:
    anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
    prompt: "Review this PR for security issues"
    claude_args: "--max-turns 10"

GitHub Actions (Max/Pro subscription):

- uses: anthropics/claude-code-action@v1
  with:
    claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
    prompt: "Review this PR for security issues"

Generate the token with claude setup-token on a machine with a browser.

GitLab CI/CD:

claude-review:
  script:
    - claude -p "Review this MR for code quality" \
        --allowedTools "Read,Grep,Glob" \
        --output-format json > review.json
  artifacts:
    paths:
      - review.json

Local Pre-Commit Hook

#!/bin/bash
# .git/hooks/pre-commit

claude -p "Check staged changes for obvious bugs" \
  --allowedTools "Bash(git diff:*),Read" \
  --max-turns 3 \
  --output-format json > /tmp/claude-check.json

if grep -q "error" /tmp/claude-check.json; then
  echo "Pre-commit check failed"
  exit 1
fi

Automation Use Cases

Automated Code Review

claude -p "Review code changes for:
  - Security vulnerabilities
  - Performance issues
  - Code style violations" \
  --allowedTools "Read,Grep,Glob" \
  --append-system-prompt "You are a senior code reviewer." \
  --output-format json

Bug Finding and Fixing

claude -p "Find all bugs in src/ that would cause runtime errors and fix them." \
  --allowedTools "Read,Edit,Bash,Glob,Grep" \
  --permission-mode "acceptEdits" \
  --max-turns 10

Documentation Generation

claude -p "Generate comprehensive API documentation from src/api. Save to docs/API.md" \
  --allowedTools "Read,Write,Glob,Grep" \
  --append-system-prompt "Generate clear, concise documentation with examples."

Multi-Turn Conversation

#!/bin/bash

# Start session
session_id=$(claude -p "Understand the auth system in src/auth/" \
  --allowedTools "Read,Glob" \
  --output-format json | jq -r '.session_id')

# Continue with follow-ups
claude -p "Check if it handles token refresh correctly" \
  --resume "$session_id" \
  --allowedTools "Read,Bash" \
  --output-format json

claude -p "Generate a security audit report" \
  --resume "$session_id" \
  --output-format json

Best Practices

CLI Usage

  1. Use --allowedTools for safe operations
  2. Use --max-turns to prevent runaway agents
  3. Parse structured output with jq
  4. Use sessions for multi-step tasks

Agent SDK Usage

  1. Use permissionMode: "acceptEdits" for development
  2. Use "bypassPermissions" for CI
  3. Stream messages for real-time feedback
  4. Use hooks for audit logging
  5. Maintain sessions for complex workflows

Quick Reference

TaskCLISDK
Quick analysisclaude -p "analyze"query(prompt="analyze")
Auto-approval--allowedTools "Read,Edit"permission_mode="acceptEdits"
Structured output--output-format jsonstructured_outputs
Context persistence--resume session_idresume=session_id
Custom validationPost-process JSONhooks={"PostToolUse": [...]}

Sources

On this page