Headless CI

Headless CI: your coding agent in GitHub Actions

Headless CI runs your coding agent in a CI job through the Context Mode gateway. You add the gateway's base URL and a few request headers to the workflow. The agent, its own install, the prompt and the model stay the same.

Key facts

What it is
Two env lines for a CI job: ANTHROPIC_BASE_URL and ANTHROPIC_CUSTOM_HEADERS. npx @context-mode/cli ci init writes them into a GitHub Actions workflow for you.
Who it is for
Teams that run a coding agent on pull requests, CI failures, issues or releases.
Clients
Claude Code headless (claude -p), anthropics/claude-code-action and the Claude Agent SDK. Codex, OpenCode and Pi connect by base URL too: their lines.
Measured result
24% to 34% lower cost per run on 4 CI jobs, Claude Code headless with Sonnet 5.5, 2026-11-01: the table and Benchmarks.
Limits
Needs a CI key, which a signed-in terminal makes. GitHub gives no secrets to pull requests from forks, so the workflow skips them. The table's figures are for Claude Code with Sonnet 5.5.

Turn it on

Run these in your repo. First sign your terminal in, once:

npx @context-mode/cli login

Make a CI key and save it as the repo secret CONTEXT_MODE_KEY. --gh saves it with the GitHub CLI. The repo comes from your git origin, or from --repo owner/name:

npx @context-mode/cli ci key create --gh

Write the workflow:

npx @context-mode/cli ci init

Add your model key as the secret ANTHROPIC_API_KEY. Then open a pull request: the review runs through Context Mode.

What ci init writes

.github/workflows/context-mode-review.yml, word for word. The only Context Mode lines are in the job's env block.

# Context Mode + anthropics/claude-code-action: Claude reviews every pull request.
# Written by `npx @context-mode/cli ci init`. The only Context Mode lines are in the job "env" block below.
# Secrets: ANTHROPIC_API_KEY (your model key, as the action needs today) and CONTEXT_MODE_KEY (a CI key from
# `npx @context-mode/cli ci key create`). This file holds no key.
name: context-mode-review

on:
  pull_request:
    types: [opened, synchronize, ready_for_review]

permissions:
  contents: read
  pull-requests: write

concurrency:
  # One review per PR at a time; a new push cancels the old review.
  group: context-mode-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    # Fork PRs get no secrets (GitHub rule), so skip them instead of failing.
    if: github.event.pull_request.head.repo.full_name == github.repository && !github.event.pull_request.draft
    runs-on: ubuntu-latest
    timeout-minutes: 20
    env:
      # --- Context Mode: start ---------------------------------------------
      # 1. Send the agent's model requests through the gateway.
      ANTHROPIC_BASE_URL: https://gateway.contextmode.ai
      # 2. One header per line. The gateway strips every x-cm-* header before it
      #    forwards, so CONTEXT_MODE_KEY never reaches Anthropic.
      #    x-context-mode-banner: off keeps the savings line out of the review text.
      #    x-cm-ci-* tell the gateway which repository, pull request and run sent the request.
      ANTHROPIC_CUSTOM_HEADERS: |
        x-cm-device-key: ${{ secrets.CONTEXT_MODE_KEY }}
        x-context-mode-banner: off
        x-cm-ci-repo: ${{ github.repository }}
        x-cm-ci-ref: pr-${{ github.event.pull_request.number }}
        x-cm-ci-run: ${{ github.run_id }}
      # 3. Claude Code turns tool search off for a non-first-party base URL; turn it back on.
      ENABLE_TOOL_SEARCH: "true"
      # --- Context Mode: end -----------------------------------------------
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          github_token: ${{ secrets.GITHUB_TOKEN }}
          prompt: |
            Review pull request #${{ github.event.pull_request.number }} in ${{ github.repository }}.
            Compare the PR head with origin/${{ github.event.pull_request.base.ref }}.
            Report only real problems: bugs, missing tests, security issues, breaking changes.
            For each one give the file, the line and a one-line fix.
            Post the result as one PR comment with `gh pr comment`. If you find nothing, say so in one line.
          claude_args: |
            --max-turns 20
            --allowedTools "Bash(git diff:*),Bash(git log:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Read,Grep,Glob"

claude -p in your own job

For a CI-failure fix, triage or release notes, put the same env block on the job. Claude Code's own installer comes first:

env:
  ANTHROPIC_BASE_URL: https://gateway.contextmode.ai
  ANTHROPIC_CUSTOM_HEADERS: |
    x-cm-device-key: ${{ secrets.CONTEXT_MODE_KEY }}
    x-context-mode-banner: off
    x-cm-ci-repo: ${{ github.repository }}
    x-cm-ci-ref: pr-${{ github.event.pull_request.number }}
    x-cm-ci-run: ${{ github.run_id }}
  ENABLE_TOOL_SEARCH: "true"
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
  - run: |
      curl -fsSL https://claude.ai/install.sh | bash
      echo "$HOME/.local/bin" >> "$GITHUB_PATH"
  - run: claude -p "The tests fail. Find the cause and fix it." --allowedTools "Bash,Read,Edit"

Claude Code turns tool search off for a base URL that is not Anthropic's, so the block sets ENABLE_TOOL_SEARCH: "true". The Claude Agent SDK reads the same env vars, with no code change.

Measured result

24% to 34% lower cost per run on 4 CI jobs. Each task ran twice, back to back: direct to Anthropic, and through the gateway with the lines ci init writes. Same agent, same prompt, same model.

CI jobSavedDirectContext ModePer 1,000 runsCheaperQualityEvidence
CI-failure fix33.9%Sonnet 5.5, 95% range 25.1 to 43.6%$0.0442$0.0281$166 of 6Tests pass 7 of 7 in bothWorkflow · Tasks
Issue triage and labels29.6%Sonnet 5.5, 95% range 21.1 to 38.8%$0.0262$0.0183$86 of 6Label F1 pass 5 of 7 direct, 4 of 7 with Context ModeWorkflow · Tasks
Release notes25.7%Sonnet 5.5, 95% range 7.2 to 42.7%$0.1333$0.0987$354 of 5Written notes, no pass markWorkflow · Tasks
@claude fix (issue to PR)23.7%Sonnet 5.5, 95% range 11.4 to 35.7%$0.0902$0.0736$176 of 6Tests pass 5 of 7 in bothWorkflow · Tasks

Claude Code headless (claude -p), Sonnet 5.5, 2026-11-01, the live gateway. Cost per run at list price, warm runs. Saved: each task gives a cost ratio, Context Mode over direct; the saving is 1 minus the geometric mean of those ratios, so it can differ from the two mean costs. Per 1,000 runs: the direct cost minus the Context Mode cost, times 1,000. Proof: the round · raw results (in the lab) · data file · the plan, written 2026-10-31 19:27 UTC, before the first run at 2026-11-01 04:22 UTC · reproduce it.

Run the same jobs in your own repo: make a copy of the lab. Your key stays with you.

Codex, OpenCode and Pi

Each agent installs its own way first. Then its gateway config adds the Context Mode lines.

Codex (codex exec)

Job env: CONTEXT_MODE_KEY: ${{ secrets.CONTEXT_MODE_KEY }} and the agent's own model key, OPENAI_API_KEY. Then the agent's own install, its gateway config and the run:

npm i -g @openai/codex
cat >> ~/.codex/config.toml <<'TOML'
model_provider = "context-mode"
[model_providers.context-mode]
name = "context-mode"
base_url = "https://gateway.contextmode.ai"
wire_api = "responses"
env_key = "OPENAI_API_KEY"
env_http_headers = { "x-cm-device-key" = "CONTEXT_MODE_KEY", "x-cm-ci-repo" = "GITHUB_REPOSITORY", "x-cm-ci-run" = "GITHUB_RUN_ID" }
TOML
codex exec "Review this pull request ..."

OpenCode (opencode run)

Job env: CONTEXT_MODE_KEY: ${{ secrets.CONTEXT_MODE_KEY }} and the agent's own model key, ANTHROPIC_API_KEY. Then the agent's own install, its gateway config and the run:

curl -fsSL https://opencode.ai/install | bash
opencode.json in the repo root:
{ "provider": { "anthropic": { "options": { "baseURL": "https://gateway.contextmode.ai/v1",
  "headers": { "x-cm-device-key": "{env:CONTEXT_MODE_KEY}", "x-context-mode-banner": "off" } } } } }
opencode run "Review this pull request ..."

Pi (pi -p)

Job env: CONTEXT_MODE_KEY: ${{ secrets.CONTEXT_MODE_KEY }} and the agent's own model key, ANTHROPIC_API_KEY. Then the agent's own install, its gateway config and the run:

npm install -g @earendil-works/pi-coding-agent
~/.pi/agent/models.json:
{ "providers": { "anthropic": { "baseUrl": "https://gateway.contextmode.ai",
  "headers": { "x-cm-device-key": "${CONTEXT_MODE_KEY}", "x-cm-client": "pi" } } } }
pi -p "Review this pull request ..."

What each header does

HeaderWhat it does
x-cm-device-keyNames your account, for usage and billing.
x-cm-ci-repoMarks the request as CI: no personal memory, cost per repo.
x-cm-ci-refThe pull request or the branch.
x-cm-ci-runCost per run, and the gap between runs that sets how long the cache lives.
x-context-mode-banner: offKeeps the savings line out of the review text.

The gateway removes every x-cm-* header before it forwards. None of them reaches the model provider.

Get started free

Your coding agent costs less and remembers your project.

Start free: 1,000 requests, every feature, no card. After you sign in, the console gives you one command to connect your coding agent.

Read the docs