The supported path is anthropics/claude-code-action@v1, installed with /install-github-app from a local Claude Code session. Authenticate with an ANTHROPIC_API_KEY secret, a CLAUDE_CODE_OAUTH_TOKEN from a subscription, or OIDC workload identity federation. Give the job a timeout, cap turns, and never point an agent at a diff from a pull_request_target trigger.
- The official action is
anthropics/claude-code-action@v1.@betais retired. - A workflow with a
promptinput runs automatically; without one it waits for@claude. - Subscriptions do work in CI through
CLAUDE_CODE_OAUTH_TOKEN, but it is tied to one person. - Never
pull_request_targetwith an agent. This is the whole security section. - Two access checks run before Claude starts: repository write access, and not a bot.
Two ways to run it, and when each is right
| Approach | Use when | Cost of it |
|---|---|---|
anthropics/claude-code-action@v1 | You want @claude mentions, PR comments, commits pushed back | A GitHub App install and its permission set |
npm install -g @anthropic-ai/claude-code then claude -p | You want a bounded, read-only job and full control of every flag | You write the plumbing yourself |
| The separate Code Review product | You want automatic PR review with no workflow file at all | Less control over prompt, model, and triggers |
Most teams should start with the action. The raw CLI is the right call for one specific shape: a job that reads a diff, emits a verdict, and is not allowed to touch anything.
Setting up the official action
Run the installer from a local session
gh auth login # the installer checks for the GitHub CLI
claude # in the repository you want to connect
# then, inside the session:
/install-github-app
It installs the Claude GitHub App, stores your credential as a repository secret, pushes a branch with the workflow files you select, and opens a pull request ready to create. You need admin access on the repository.
Or do it by hand
Install the Claude GitHub App, add the secret yourself, and copy examples/claude.yml from the action repository into .github/workflows/. Use this path when you do not run Claude Code locally or you want to own the workflow files outright.
Pick an authentication method
# an API key from the Claude Console
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# or a subscription token, generated locally with: claude setup-token
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
- To avoid a long-lived secret entirely, use OIDC workload identity federation: set
anthropic_federation_rule_idandanthropic_organization_id, and grant the jobid-token: write. - Rolling out across an organisation: install the app once at organisation level, store the secret as an organisation Actions secret, and define the job once as a reusable workflow.
Interactive mode, automation mode, and who may trigger a run
The action decides how to run from your configuration, and the difference is one input.
| Interactive | Automation | |
|---|---|---|
| Configuration | No prompt input | A prompt input is set |
| Starts on | The trigger phrase, @claude by default | Any GitHub event, including schedule |
| Results appear | As a comment on the issue or PR | In the workflow run log |
| Typical use | Ask Claude to implement or explain something | Review, triage, scheduled reports |
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write
pull-requests: write
issues: write
id-token: write # required for the action's GitHub App auth
actions: read # lets Claude read CI results on the PR
steps:
- uses: actions/checkout@v6
with: { fetch-depth: 1 }
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--max-turns 15
--model claude-sonnet-5
The trigger that will get you compromised
The difference is not cosmetic.
| Trigger | Runs code from | Has secrets? | Safe with an agent? |
|---|---|---|---|
pull_request | The PR branch | No, for forks | Yes |
pull_request_target | The base branch | Yes | No |
The defence is capability, not prompting. An agent with read-only tools and a token that cannot merge cannot act on an instruction hidden in a diff, whatever that instruction says. Do not attempt to solve this with a system prompt telling the model to ignore instructions in code it reads: that is a request, and the whole problem is that requests are not boundaries.
- On public repositories GitHub withholds secrets from fork-triggered runs entirely, so a review workflow on
pull_requestruns only for branches in the same repository. That is the correct behaviour, not a bug to work around. - Give the reviewer job
contents: readandpull-requests: read, and let a separate step post the comment. - Restrict tools explicitly with
--allowedTools "Read,Grep,Glob"inclaude_args. Default permissions are designed for a human sitting there. - Pass untrusted text through an environment variable and a file rather than interpolating it into a shell command, which also closes the ordinary shell-injection hole that exists here independently of the agent.
A bounded read-only job with the raw CLI
When you want a verdict rather than a collaborator, the CLI is smaller and every knob is visible.
name: agent-review
on:
pull_request: # NOT pull_request_target
permissions:
contents: read
pull-requests: write
concurrency:
group: agent-review-${{ github.ref }}
cancel-in-progress: true
jobs:
review:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
with: { fetch-depth: 0 }
- run: npm install -g @anthropic-ai/claude-code@2.1.219
- name: Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DISABLE_AUTOUPDATER: "1"
run: |
git diff origin/${{ github.base_ref }}...HEAD > diff.patch
claude -p "Review this diff. Report only defects that will actually
occur, each with the triggering input. No style comments.
If nothing is wrong, say so in one line." \
--allowedTools "Read,Grep,Glob" \
--model claude-sonnet-5 \
--output-format json < diff.patch > out.json
jq -r '.result' out.json > review.md
echo "cost: \$$(jq -r '.total_cost_usd' out.json)" >> $GITHUB_STEP_SUMMARY
- name: Comment
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr comment ${{ github.event.number }} --body-file review.md
Keeping the bill bounded
Every run consumes two things: GitHub Actions minutes on the runner, and tokens against your key or subscription. Both are unattended, which is exactly why they run away.
| Control | Where | Why |
|---|---|---|
timeout-minutes | The job | A stuck agent bills until something kills it |
--max-turns | claude_args | Caps how much work a single run can do |
--model claude-sonnet-5 | claude_args | Without it you inherit the default model |
| Console spend limit | Claude Console workspace | The only hard ceiling that exists |
concurrency group | The workflow | Stops ten pushes queueing ten agents |
| Skip drafts and bots | The if: condition | Most draft pushes do not need a review |
Log total_cost_usd | The JSON result | Per-run cost for free, from output you already have |
The two failures people actually hit.
| Symptom | Cause |
|---|---|
| CI does not run on Claude commits | You passed github_token: secrets.GITHUB_TOKEN. GitHub never triggers workflows on commits made with the default token. Remove it so the action authenticates as the app |
@claude does nothing | App not installed, workflows disabled, the comment says @claude-bot, or the commenter has no write access |
Questions people ask
Run /install-github-app from a local Claude Code session in the repository. It installs the Claude GitHub App, stores your credential as a repository secret, and opens a pull request with the workflow files. You need admin access on the repository and the GitHub CLI authenticated.
Yes. Generate a token locally with claude setup-token and store it as CLAUDE_CODE_OAUTH_TOKEN, then pass it to the claude_code_oauth_token input. It works on Pro, Max, Team, and Enterprise plans. The token is tied to one person subscription, so use a Console API key for anything shared across repositories.
With the pull_request trigger and read-only tools, yes: GitHub withholds secrets from fork-triggered runs on public repositories. With pull_request_target it is not, because that runs with your secrets against a diff an outsider controls.
By capability, not prompting. Read-only tools, a token that cannot merge, and never pull_request_target. An injected instruction is unactionable regardless of what it says when the agent has no tool that could act on it.
Check the app is installed, workflows are enabled, the secret is set, the comment contains @claude as a complete word rather than /claude or @claude-bot, and the commenting user has write access to the repository.
GitHub does not trigger workflows on commits made with the default GITHUB_TOKEN. If you passed github_token: secrets.GITHUB_TOKEN to the action, remove it so it authenticates as the Claude GitHub App, or pass a custom app token instead.
Typically a few cents to a few tens of cents on Sonnet for a normal diff. The JSON output reports total_cost_usd per run, so log it to the step summary and measure yours rather than estimating.
Change @beta to @v1, remove the mode input because the action now detects the mode from whether you set a prompt, replace direct_prompt with prompt, and move CLI options such as max_turns and model into claude_args.
Sources
Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.