Claude Certification Guides
CCAR-F/Domain 39 min read

Lesson 3.6

Headless Mode, SDK and CI/CD Integration

Running Claude Code without a terminal user: the print mode flags, output formats, permission strategy for unattended runs, and GitHub Actions integration.

Headless mode runs Claude Code as a non-interactive command: you pass a prompt, it works, it prints a result and exits. This is the basis for scripting, scheduled jobs, pull-request automation and the Agent SDK, which wraps the same engine. The architectural challenge is that there is no human to answer permission prompts, so the permission strategy, budget limits and output parsing must be decided in advance. The exam tests the flags and the trade-offs.

Print mode basics

claude -p "Summarise the open TODO comments in src/ as a markdown table"

-p (or --print) makes the run non-interactive. Useful companions:

FlagPurpose
--output-format text|json|stream-jsonPlain text, a single JSON result with cost and session ID, or newline-delimited JSON events as they happen
--input-format stream-jsonAccept a stream of messages on stdin for multi-turn automation
--allowedTools "Read,Bash(npm test:*)"Pre-approve tools for this run
--disallowedTools "Write,Edit"Remove tools for this run
--permission-mode acceptEdits|plan|dontAsk|bypassPermissionsSet the posture
--max-turns NCap agentic iterations
--model, --fallback-modelChoose the model and a fallback on overload
--system-prompt, --append-system-promptReplace or extend the default system prompt
--resume <id>, --continueContinue an earlier headless session
--mcp-config, --strict-mcp-configExact MCP servers for the run
--verboseFull turn-by-turn log for debugging

Piping works in both directions: cat error.log | claude -p "Explain the root cause" feeds stdin as context, and --output-format json gives a parseable result with fields such as result, session_id, total_cost_usd, num_turns and is_error. stream-json emits the init, assistant, user and result events the Agent SDK exposes as typed messages, which is what to use when a wrapper needs progress updates.

Permissions without a human

Three strategies exist, in order of preference:

  1. Allow list plus dontAsk. Enumerate exactly what the job needs (Read, Grep, Bash(npm test:*)) and deny everything else by default. The run fails fast if Claude attempts anything unexpected.
  2. Programmatic approval. --permission-prompt-tool names an MCP tool that is called whenever a permission prompt would appear; your tool implements the policy (or asks a human through a chat system) and returns allow or deny. A PermissionRequest hook can do the same from settings.
  3. Bypass in a sandbox. --dangerously-skip-permissions inside an ephemeral container with no production credentials, scoped tokens and no network beyond what the job needs. The container is the guardrail.

Never run bypass mode on a developer machine or a shared runner with live credentials. The flag's name is the policy.

Budgets and determinism

Unattended runs need hard limits: --max-turns for iterations, a budget cap where the version supports it, a job-level timeout from the CI system, and --strict-mcp-config so the server set is exactly what the pipeline declares. Pin the model explicitly rather than inheriting a developer's default, and version the prompt in the repository so changes are reviewable.

GitHub Actions

The official anthropics/claude-code-action runs Claude Code inside a workflow. The simplest setup comes from /install-github-app in a session, which installs the GitHub App and generates a workflow. A trimmed example:

name: Claude
on:
  issue_comment: { types: [created] }
  pull_request_review_comment: { types: [created] }
jobs:
  claude:
    if: contains(github.event.comment.body, '@claude')
    runs-on: ubuntu-latest
    permissions: { contents: write, pull-requests: write, issues: write, id-token: write }
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 1 }
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: "--max-turns 15 --allowedTools Read,Edit,Bash(npm test:*)"

The action responds to @claude mentions in issues and pull requests, or runs a fixed prompt on a schedule or on push for tasks such as automated review. claude_args passes through the same flags as the CLI. Credentials can also come from cloud providers (Bedrock, Vertex AI or Foundry) through OIDC rather than a stored API key. GitLab CI and other systems run the CLI directly in a container with the same flags.

The Agent SDK relationship

The Agent SDK is the programmatic form of headless mode. query() in Python or TypeScript launches the same engine, and the SDK's typed messages correspond to --output-format stream-json. Choose the SDK when the orchestration logic lives in your application (custom tools, hooks as functions, dynamic prompts); choose the CLI when a shell step in a pipeline is enough. Note that the SDK does not load project settings or CLAUDE.md unless settingSources asks for them, whereas the CLI does.

Exam focus

  • claude -p runs non-interactively; --output-format json returns a parseable result with session_id and cost, and stream-json emits events as they occur.
  • Unattended permission strategy, in order of preference: allow list with dontAsk, programmatic approval via --permission-prompt-tool or a PermissionRequest hook, then bypass inside an ephemeral sandbox only.
  • --max-turns, job timeouts, a pinned model and --strict-mcp-config make runs bounded and reproducible.
  • anthropics/claude-code-action is the official GitHub Actions integration; /install-github-app bootstraps it; claude_args carries CLI flags.
  • Credentials in CI come from secrets or cloud OIDC, never from a committed file.
  • The Agent SDK wraps the same engine as headless mode but loads no filesystem settings by default.

Check yourself

5 questions

  1. 1.A CI job needs Claude Code's answer in a machine-readable form and must record the session identifier for a later step. Which command is correct?

  2. 2.Which flag runs Claude Code non-interactively so it processes one prompt and exits?

  3. 3.A pipeline runs claude -p inside the checkout of an untrusted pull request. Hooks defined in the PR's .claude/settings.json and servers in its .mcp.json execute during the run. What is the recommended mitigation?

  4. 4.A team wants a locked-down headless run in which Read, Grep and Glob are pre-approved and anything else that would prompt is denied rather than hanging. Which flags achieve this?

  5. 5.A script needs Claude Code's headless output to conform to a specific JSON schema so it can be parsed without heuristics. What should it use?