Set Up Claude as a PR Reviewer with the Official GitHub Action
A hardened, paste-ready setup for adding Anthropic's claude-code-action to a GitHub repo, with the security and cost knobs spelled out for production use.
Small teams with one bottleneck reviewer end up merging PRs late, with the reviewer skimming at the end of the day. The boring class of bugs (missing null check, swallowed error, wrong logger, a test added but never executed) is exactly what a careful first-pass reader catches in thirty seconds, and exactly what a tired human glides past. The Anthropic-maintained anthropics/claude-code-action is the right default for an AI first-pass reviewer today: lowest-friction setup, inherits Anthropic's auth and prompt-injection hardening, and composes with normal branch protection without itself becoming a required reviewer.
This post is for backend and full-stack engineers who already write GitHub Actions YAML and want a paste-ready setup, plus the security and cost levers to make it safe on a production repo.
The recommended setup
Drop the workflow at .github/workflows/claude-review.yml. The file below is the minimum I'd ship to a real repository: triggered on pull_request (not pull_request_target), with the minimum permission set the action needs (contents: read, plus pull-requests: write and issues: write for posting comments), a tight claude_args block, and the model and turn limit pinned.
The two non-obvious choices are worth naming. First, fetch-depth: 0 on the checkout step gives the action access to the full diff history; without it, large PRs get truncated context. Second, claude_args is the v1 way to pass CLI flags through to the underlying Claude Code runtime. In v0.x these were top-level inputs (model, max_turns, allowed_tools); pasting a v0.x example into a v1 workflow looks like it works because GitHub Actions does not error on unknown inputs, but the values silently no-op. Always check the action's migration guide if you copy from a tutorial older than the v1 GA.
To install the secret without leaving the editor, run claude /install-github-app from a local Claude Code session. This walks you through installing the official GitHub App at https://github.com/apps/claude and writes ANTHROPIC_API_KEY to repo secrets. You need repo admin to do this; if you don't, ask whoever does to set the secret manually and skip the GitHub App.
The interactive @claude loop
The first job handles automation: every PR open and push gets a review. The second job handles interactive questions. When a teammate replies to Claude with @claude please re-check the auth flow after my last commit, the action picks up the mention and replies in-thread.
The mode switch is automatic in v1. When the action is triggered by pull_request and a prompt is set, it runs in automation mode. When triggered by issue_comment or pull_request_review_comment without a prompt, it listens for @claude mentions and runs in interactive mode.
You'll also want to add issue_comment and pull_request_review_comment to the on: block at the top of the file for this job to fire. The github.event.issue.pull_request guard on the issue_comment branch is load-bearing: GitHub fires issue_comment for both issues and pull request threads, and that property is non-null only on PR threads, so without it the action would also reply to plain-issue mentions and burn tokens on unrelated discussions. The action posts as a regular GitHub comment, with optional progress checkboxes that update live when you opt in via track_progress: true. It can also leave inline review comments and suggestion blocks when the prompt asks for them.
Cost
A useful heuristic: input tokens are roughly the diff length in lines times four, plus whatever the model pulls in via Read and Grep for context. Output is the length of comments produced. A 200-LOC, 5-file PR is typically a few thousand input tokens and a few hundred output. A 1500-LOC, 30-file PR can blow past 50K input on a thorough review. Prompt caching helps on repos reviewed repeatedly, since the action can re-use the cached repo context.
Verify pricing on the Anthropic pricing page before you publish numbers internally; rates change. As of this writing:
Sonnet is the right default for general review: enough depth to catch real issues without the Opus tax. Haiku makes sense on high-volume monorepos where fast feedback beats deeper analysis. Opus is worth it on security-sensitive code where missed bugs are expensive. The cost-control levers worth setting from day one:
--max-turns 5caps the agent loop so a confusing PR can't iterate indefinitely.- A
paths:filter on the workflow trigger skips review on docs-only PRs. - A
[skip-claude]commit-message convention via anif:guard lets authors opt out for a trivial change. --fallback-modelinclaude_argsdrops to a cheaper model when the primary is rate-limited; CI bursts on Monday morning are the obvious trigger.
Security hardening
This is the section that justifies the post. The defaults above are safe, but it's worth knowing why each one is set the way it is, because the wrong values are easy to copy-paste from older tutorials.
Use pull_request, not pull_request_target. The latter runs in the context of the base branch with secrets available, which means a PR from a fork can run untrusted code with access to ANTHROPIC_API_KEY. This is the canonical "pwn request" exfiltration class GitHub Security Lab has documented at length. The action's built-in access control (it only triggers for users with write access to the repo) protects you on pull_request events; don't override it with allowed_non_write_users, which the docs explicitly call risky.
Don't grant contents: write. The default review job needs contents: read plus pull-requests: write and issues: write for posting comments. Adding contents: write gives the model the power to push to your branch, which you do not want unless you have a deliberate workflow that asks Claude to commit fixes. Add id-token: write only when you're using OIDC to AWS Bedrock or Google Vertex.
Set an explicit allowed_bots list, never '*'. GitHub Apps and bots cannot trigger the action by default. If you want Claude to run after Dependabot or a release bot, list the bot accounts explicitly. The docs warn that on a public repo, allowed_bots: '*' lets any GitHub App invoke the action with a prompt that the App controls, which is the same exfiltration shape as pull_request_target.
Mitigate prompt injection from external content. The action sanitizes HTML comments, invisible characters, image alt text, and hidden HTML attributes from input. The docs are upfront that new bypass techniques may emerge, so two extra knobs are worth using on public repos: include_comments_by_actor to allowlist whose comments reach Claude, and exclude_comments_by_actor to drop noisy bot comments like dependabot[bot] and renovate[bot]. Exclusion takes priority when an actor matches both.
Constrain --allowedTools. The biggest mistake in a public-facing AI workflow is leaving the tool allowlist open and getting open-ended Bash. A read-only review allowlist is Read,Grep,Glob. If you need git, narrow it: Bash(git diff:*) rather than Bash. Never grant unrestricted Bash on PR-triggered runs; the model can curl your secrets out of the runner if it's tricked into doing so.
Don't let claude[bot] count as a required reviewer. In branch protection rules, configure required reviewers as humans only. The AI review is advisory; the merge gate is human. This is the difference between a useful first pass and an automated rubber stamp.
Scope the secret. Set ANTHROPIC_API_KEY at the repo or environment level rather than org-wide. One compromised repo shouldn't burn the whole org's quota.
Pin the action. @v1 is fine for casual repos. For production or regulated repos, pin to a commit SHA so a compromised tag can't silently swap the action's behavior under you. This is standard supply-chain hygiene and applies to every third-party action you use.
When to drop down to claude-code-base-action
The top-level action wraps a lower-level action with GitHub-specific PR and issue logic. Most teams should never need to leave the top-level wrapper. Reach for the base action when one of these is true:
- You want a GitHub status check that gates merge (pass/fail), not a PR comment. The base action exposes a
conclusionoutput (successorfailure) you can pipe into a downstreamif:or use withactions/github-script. - You want a multi-step pipeline: lint, then claude review, then integration tests, with the review's structured output feeding the next step.
- You need a non-comment artifact: emit findings as a JSON file, upload as a workflow artifact, or feed to a SIEM.
- You want the model to write fixes to a branch and open a PR back at you with custom commit-message conventions.
- You're stitching multiple Claude calls (one for security, one for performance) into a single review pass with deduplication.
The base action also exposes inputs the top-level action hides behind claude_args: system_prompt, append_system_prompt, disallowed_tools, settings, and claude_env. Useful when you want to harden the system prompt against jailbreak attempts or pass scoped env vars to MCP servers.
Common pitfalls
A short list of things I've watched go wrong:
- Using
pull_request_targetbecause an old tutorial used it, and exposingANTHROPIC_API_KEYto forked code. - Granting
contents: write"just in case" and giving the model push access to the default branch. - Leaving
allowedToolsunset and getting open-endedBashin a public repo. - Letting the action's bot push count as a required reviewer in branch protection.
- Re-running the action on every push to a long-running PR branch and burning tokens. Gate on
synchronizeif you must, but consider apaths:filter or a[skip-claude]opt-out. - Forgetting
fetch-depth: 0onactions/checkoutand getting a truncated diff. - Pricing surprise on a monorepo with 5000-LOC PRs. Cap with
--max-turnsor skip the workflow on large diffs.
Closing
The default workflow above holds for most teams: PR-triggered review, interactive @claude loop, Sonnet model, read-only tool allowlist, pull_request trigger, hard human gate on merge. Drop down to claude-code-base-action only when the output shape stops fitting (status check, file artifact, multi-step pipeline). The single next step worth taking is to git apply the workflow above into a low-stakes repo, open a small PR against it, and watch how the review reads before rolling it out broadly.
References
- anthropics/claude-code-action - The v1 README is the source of truth for triggers, automatic mode detection, and the
promptplusclaude_argsschema. - Action security documentation - Authoritative guidance on access control,
allowed_bots, prompt-injection mitigations, and commit signing. - Action setup guide - Manual setup including custom GitHub Apps as an alternative to
/install-github-app. - Action solutions guide - Ready-to-use automation patterns for security-focused review, path-specific review, and external-contributor handling.
- Action migration guide - v0.x to v1.0 input changes; flag this before pasting any pre-v1 community example.
- anthropics/claude-code-base-action - Lower-level wrapper for status checks, file artifacts, and non-comment outputs.
- Claude Code GitHub Actions docs - Anthropic's first-party how-to with Bedrock and Vertex OIDC examples.
- Anthropic API pricing - Per-MTok rates for the Claude model line; verify currency before quoting numbers in your own docs.
- GitHub Actions: Security hardening - First-party guidance on
pull_request_target,permissions:, secret handling, and SHA pinning. - GitHub Security Lab: Preventing pwn requests - The canonical write-up of the
pull_request_targetexfiltration class. - GitHub Actions: Assigning permissions to jobs - Reference for minimum-permission discipline.