Kipt

Claude Code headless mode: run claude -p in scripts and CI

Claude Code headless mode explained: run claude -p in scripts and CI, get json or stream-json output, pre-approve tools, use bare mode and continue sessions.

By the Kipt team · Updated 24 October 2026

Claude Code headless mode means running Claude Code without its chat interface: you add -p to the claude command, pass a prompt, and it does the task, prints the result and exits. That makes it usable from shell scripts, package.json scripts, cron jobs and CI pipelines, with output as plain text, JSON or a stream of JSON events.

Anthropic's documentation now files this under the Agent SDK. The Run Claude Code programmatically page says the SDK "gives you the same tools, agent loop, and context management that power Claude Code," and is available "as a CLI for scripts and CI/CD, or as Python and TypeScript packages." This guide covers the CLI side.

How do you run Claude Code in headless mode?

Add -p (or its long form --print) to any claude command:

claude -p "What does the auth module do?"

A few behaviors matter for scripting:

  • Exit codes. Claude Code exits with 0 on success and a non-zero code when the run fails, so scripts can branch on it. An invalid flag is reported on stderr before the run starts. A failure inside the run, such as missing authentication, is printed as the result on stdout.
  • Stdin works. You can pipe data in and redirect the answer out. Piped stdin is capped at 10MB; for bigger inputs, write a file and name its path in the prompt.
  • Not every flag combines with -p. The docs say Claude Code rejects --bg, and rejects --cloud with a task description.
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt

Inside -p, user-invoked skills and custom commands still work: put /skill-name in the prompt and Claude Code expands it. Commands that only run in the terminal interface, such as /login, are not available. /model, /effort and a few others accept a value as an argument, for example /model sonnet.

What output formats does claude -p support?

--output-format takes three values:

Format What you get Use it for
text (default) Plain text Reading in a terminal, writing to a file
json One JSON object with result, session ID and metadata Scripts that parse the answer or track cost
stream-json Newline-delimited JSON events Live progress in your own UI or logs

With json, the payload includes total_cost_usd and a per-model cost breakdown. The docs note both are client-side estimates and can differ from your bill. A typical pattern pipes the result through jq:

claude -p "Summarize this project" --output-format json | jq -r '.result'

Structured output with a schema

When you need a fixed shape, combine --output-format json with --json-schema. The validated data lands in a structured_output field:

claude -p "Extract the main function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'

If the run ends without valid structured output, for example because Claude declined, the field is missing. Check that it exists before your script uses it.

Streaming events

For token-by-token output, use stream-json together with --verbose and --include-partial-messages:

claude -p "Explain recursion" --output-format stream-json --verbose --include-partial-messages

Each line is a JSON event. The first is usually system/init, which reports the model, tools, MCP servers and loaded plugins. The last line is a result message with the final text, cost and session metadata. Retryable API failures show up as system/api_retry events, and messages from subagents carry a parent_tool_use_id so you can tell which run they belong to.

How do you let headless Claude Code use tools without prompts?

Nobody is there to click "allow" in a headless run, so you decide up front what Claude may do. This changed recently: the permission modes docs say that in Claude Code v2.1.302 or later, claude -p runs with no permission mode set start in Manual mode, and "calls needing approval are denied" unless an allow rule, hook or --permission-prompt-tool approves them.

You have three main levers.

1. --allowedTools for specific tools. List the tools Claude can use without asking:

claude -p "Run the test suite and fix any failures" \
  --allowedTools "Bash,Read,Edit"

The flag uses permission rule syntax, so you can scope it tightly. The trailing * is a prefix match, and the docs point out that the space matters: Bash(git diff *) matches git diff ... but not git diff-index.

claude -p "Look at my staged changes and create an appropriate commit" \
  --allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"

2. --permission-mode for a baseline. Instead of listing tools, set a mode for the whole run:

  • auto: a classifier reviews most actions instead of you.
  • dontAsk: every call that would prompt is denied. Reads in your working directories, the read-only command set and anything you pre-approved still run. Good for locked-down CI.
  • acceptEdits: file writes and common filesystem commands like mkdir and mv run without asking; other shell commands still need an allow rule.

3. --permission-prompts none for unattended jobs. Requires v2.1.259 or later. Anything that would prompt is denied, Claude is told nobody can approve it and not to retry, and the run continues. Tools that need a person, such as AskUserQuestion, are removed.

To restrict which tools exist at all, rather than which are pre-approved, use --tools (for example --tools "Bash,Edit,Read") or remove tools with --disallowedTools. Two more flags cap a run: --max-turns limits agentic turns and --max-budget-usd stops once estimated spend reaches an amount. Both are print mode only, per the CLI reference.

What is bare mode and when should you use it?

By default, claude -p loads everything an interactive session would: hooks, skills, plugins, MCP servers, auto memory and CLAUDE.md, from the project and from ~/.claude. That means a teammate's personal hook or a project's .mcp.json server can change what your script does. The docs also warn that a -p session shows no workspace trust dialog, so it runs a project's hooks even in a folder you have never trusted.

--bare skips all that auto-discovery so the run is the same on every machine:

claude --bare -p "Summarize README.md" --allowedTools "Read"

In bare mode, Claude has the Bash, file read and file edit tools, and you pass anything else explicitly: --append-system-prompt, --settings, --mcp-config, --agents or --plugin-dir. Bare mode also never reads OAuth credentials or the system keychain, so you need ANTHROPIC_API_KEY (or an apiKeyHelper in --settings) for the Anthropic API.

The docs call --bare "the recommended mode for scripted and SDK calls" and say it "will become the default for -p in a future release." If you write CI jobs today, add it now.

How do you use claude -p in CI?

Most CI use falls into a few shapes.

A project-specific linter. Pipe a diff in and ask for a narrow check. Piping means Claude needs no Bash permission to read the diff:

{
  "scripts": {
    "lint:claude": "git diff main | claude -p \"you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\""
  }
}

A review script with a custom role. --append-system-prompt adds instructions while keeping Claude Code's default behavior:

gh pr diff "$1" | claude -p \
  --append-system-prompt "You are a security engineer. Review for vulnerabilities." \
  --output-format json

Fail the build when setup breaks. The system/init event lists plugin_errors and mcp_server_errors. A server with an invalid --mcp-config entry is skipped and the run still exits cleanly, so a CI gate should fail on a non-empty error array rather than trust the exit code alone.

Multi-step jobs. Use --continue to keep going in the most recent conversation, or capture the session ID and --resume it:

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

For pull request automation on GitHub, Anthropic's GitHub Actions integration wraps this for you; see the Claude Code GitHub Actions guide. Pick the model per job with --model, covered in the Claude Code models guide.

When should you use the Agent SDK instead of claude -p?

claude -p is ideal when a shell script or CI step is the whole program. When Claude Code is one part of a bigger application, the Python and TypeScript packages fit better. The headless docs list what they add: "structured outputs, tool approval callbacks, and native message objects." With callbacks, your code decides each permission request at runtime instead of fixing a list up front.

Both run the same loop, so a prompt and tool list that work with claude -p carry over. The Claude Agent SDK guide walks through the packages.

Keep the prompts your scripts depend on

Headless jobs live or die on their prompts: the review brief, the typo-linter instruction, the commit message rules. Kipt keeps prompts like these as Markdown in a private GitHub repo you own, with {{name}} blanks for the parts that change, so the version your CI uses is the same one you search with Cmd+K. If a prompt is really a reusable procedure, the free SKILL.md generator turns it into a skill your -p runs can call with /skill-name.

Questions

What is Claude Code headless mode?

Headless mode is Claude Code running without its interactive interface. You pass a prompt with the -p (or --print) flag, Claude Code runs the task, prints the result and exits. Anthropic's docs now describe this as using the Agent SDK through the CLI.

How do I get JSON output from claude -p?

Add --output-format json. The response includes the text in a result field plus the session ID and metadata such as cost. For output that matches your own shape, add --json-schema with a JSON Schema and read the structured_output field.

Why are tool calls denied when I run claude -p?

Since Claude Code v2.1.302, claude -p runs with no permission mode set start in Manual mode, where calls that need approval are denied. Pre-approve tools with --allowedTools, or start in auto mode with --permission-mode auto so a classifier reviews each action.

Does claude -p use my Claude subscription or an API key?

Both are possible. In -p mode, Claude Code always uses ANTHROPIC_API_KEY when it is set, which can override your subscription login. Bare mode (--bare) never reads OAuth credentials or the keychain, so it needs ANTHROPIC_API_KEY or an apiKeyHelper.

What is the difference between claude -p and the Agent SDK?

They share the same agent loop and tools. claude -p is the command line form for scripts and CI. The Python and TypeScript Agent SDK packages add structured outputs, tool approval callbacks and native message objects for building your own applications.

More in Claude Code

All guides

Keep your prompts and skills in one place, in your own GitHub. Install every skill with one command.

Get started

Kipt is an independent product, not affiliated with or endorsed by Anthropic, OpenAI, Cursor or GitHub. Claude, ChatGPT, Codex, Cursor and GitHub are trademarks of their owners.