Claude Code GitHub Actions PR Review: Minimal Workflow
Set up the Claude Code GitHub Action for PR review: a minimal workflow file, secrets handling, safe permissions, and fixes when it won't run.

To run Claude Code in GitHub Actions for pull request review, install the Claude GitHub App, store an ANTHROPIC_API_KEY secret, and commit a workflow that calls anthropics/claude-code-action@v1 on pull_request events. The fastest route is /install-github-app inside claude. The manual route is about thirty lines of YAML.
The safe version uses read-only permissions on the job, a secret passed through ${{ secrets.* }}, and a review that only runs for branches in your own repository. Everything below is that setup, plus the mistakes that make it silently do nothing.
What the Claude Code GitHub Action does#
Claude Code GitHub Actions runs Claude Code inside your repository's workflows. You can use it two ways:
- Interactive mode: someone mentions
@claudein an issue or PR comment, and Claude analyzes code, implements changes, or pushes commits. - Automation mode: you give the action a
promptand a trigger event, and it runs on its own. PR review is the common case.
Several products share the Claude Code name, so check you're reading about the right one. This article covers the claude-code-action integration you configure with workflow files. The docs list separate products for automatic review without a workflow (Code Review) and for cloud sessions.
Quick setup with /install-github-app#
Install the GitHub CLI and run gh auth login first. Then open claude in the repository and run:
/install-github-app
Follow the prompts. Claude Code installs the Claude GitHub App, saves your credential as a repository secret, pushes a branch with the workflow files you select, and opens a pull request for you to create. Merge it and @claude works in the repository.
Two limits to know. This command works only with github.com repositories; on GitLab or Bitbucket remotes it prints a notice and exits. And if you use Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, quick setup isn't the path; the docs have a separate cloud providers page.
If you pick the review workflow, Claude posts each review on the pull request. Before v2.1.229 it wrote the review only to the workflow run log, so an old generated file may look like it does nothing.
A minimal PR review workflow file#
This is the review workflow from the docs. Save it as .github/workflows/code-review.yml.
name: Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
What each non-obvious part does:
pluginsandplugin_marketplaces: install thecode-reviewplugin. Thepluginsvalue isplugin-name@marketplace-name, and the marketplace name comes from the marketplace's own manifest, not its repository URL.prompt: accepts a skill invocation as well as plain text. Here it runs the plugin's namespaced skill against the current PR.--comment: without it Claude posts nothing and you read findings in the run log. With it, you get an inline comment on each issue, or one summary comment when it finds none.claude_argswith--allowedTools: keep it. The action starts the MCP server that posts inline comments only when--allowedToolsinclaude_argsnames that tool, even though the skill's own frontmatter names it too.id-token: write: required for the default GitHub App authentication.
Claude skips draft and closed PRs, PRs it judges don't need review (automated or trivial ones), and PRs that already have a comment from Claude. If a PR gets no review, that's the first thing to check.
Secrets handling#
Pick one authentication secret and add it under the repository's Actions secrets:
| Secret | Input to pass it to | Where it comes from |
|---|---|---|
ANTHROPIC_API_KEY |
anthropic_api_key |
A Claude API key from the Claude Console |
CLAUDE_CODE_OAUTH_TOKEN |
claude_code_oauth_token |
Run claude setup-token locally (Pro, Max, Team, Enterprise plans) |
If you use the OAuth token, change the workflow line to:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
Rules I'd follow:
- Never paste a key into the YAML. Always reference
${{ secrets.NAME }}. - Use one credential, not both, so it's obvious which one a run is billing.
- On public repositories, GitHub withholds secrets from runs triggered by fork pull requests. The review above therefore runs only on PRs from branches in the same repository. Treat that as a feature, not a bug. Don't work around it with a trigger that hands secrets to fork code.
For how GitHub stores and exposes secrets, see GitHub's guide to using secrets in GitHub Actions.
Safe permissions#
There are two layers: the job's permissions: block in your YAML, and the permissions you grant the Claude GitHub App at install time.
For review only, the job block can stay read-only on contents, pull requests and issues, as in the file above. The @claude workflow that implements changes needs more:
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
actions: read lets Claude read CI results on PRs. Give write access only to a workflow that must push commits, and keep review and implementation in separate files if you can.
The app is another matter. The Claude Code GitHub Action relies on three app permissions: Contents, Issues and Pull requests, each read and write. But the app is shared by other Claude features, so installing it grants a larger set that includes Actions, Checks, Workflows, Discussions and more. GitHub doesn't let you accept a subset. If your organization wants only the three, the docs say to create a custom GitHub App with Contents, Issues and Pull requests, following the setup guide. A custom app covers only this Action, not Code Review or web auto-fix.
Check your workflow before you push#
A tiny script can catch the mistakes that cost a CI round trip. This one scans a workflow string for a hardcoded key, a missing id-token, and write permissions in a review job. Swap in your own YAML text.
import re
workflow = """
name: Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: sk-ant-example
prompt: "/code-review:code-review"
"""
problems = []
m = re.search(r"anthropic_api_key:\s*(.+)", workflow)
if m and "secrets." not in m.group(1):
problems.append("API key is not read from secrets")
if "id-token: write" not in workflow:
problems.append("missing id-token: write (needed for default app auth)")
if re.search(r"(contents|pull-requests|issues):\s*write", workflow):
problems.append("write permission in a review workflow")
if "--comment" not in workflow:
problems.append("no --comment: review will only appear in the run log")
if "--allowedTools" not in workflow:
problems.append("no --allowedTools: inline comment server won't start")
print("\n".join(problems) if problems else "looks fine")
Add @claude mentions to the same repo#
Once review works, the interactive workflow is a separate file. It uses comment events and an if so runners don't start on unrelated comments:
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
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
Without a prompt, Claude responds to the trigger phrase, which defaults to @claude and can be changed with trigger_phrase. Put review criteria and code style in a CLAUDE.md in the repository root so both workflows follow them.
Common mistakes and "not working" fixes#
- Review runs but nothing appears on the PR. You left out
--comment, or your generated workflow predates v2.1.229. Add the--commentargument and theclaude_argsline from the example above. - Inline comments never post.
--allowedToolsisn't inclaude_args. The skill's frontmatter alone doesn't start the MCP server. - The job fails at authentication. Check
id-token: writeis in the job'spermissions, and that the secret name matches the input (anthropic_api_keyvsclaude_code_oauth_token). - No review on a specific PR. It may be a draft, closed, trivial, automated, or already commented on by Claude.
- Fork PRs fail on a public repo. GitHub withholds secrets from fork runs. That's expected.
- CI doesn't run on Claude's commits. GitHub doesn't trigger workflows on commits made with the default
GITHUB_TOKEN. If you passgithub_token: ${{ secrets.GITHUB_TOKEN }}, remove it so the action authenticates as the Claude GitHub App, or pass a custom app token. Also confirm your CI triggers includepushorpull_request. /install-github-appexits immediately. Your remote isn't on github.com.- An old generated file behaves differently. Rerun
/install-github-appand choose Update workflow file with latest version when aclaude.ymlalready exists.
Pricing, limits and other questions#
The source docs don't list per-run costs, so I won't guess numbers. What they do tell you: you authenticate with either a Claude API key or a Claude subscription token, and the job runs on a GitHub runner, so both your Claude plan and your GitHub Actions usage apply. For runner limits such as job time, see the Actions limits article in the GitHub Actions documentation.
For MCP, the review example already uses it: the inline comment tool is an MCP server the action starts when you allow it. For skills, put them in .claude/skills/ and run actions/checkout first so the files exist on the runner, then pass /skill-name as the prompt.
Quick checklist#
- Install the Claude GitHub App (or run
/install-github-app). - Add
ANTHROPIC_API_KEYorCLAUDE_CODE_OAUTH_TOKENas a repository secret. - Commit the review workflow with read-only job permissions plus
id-token: write. - Keep
--commentinpromptand--allowedToolsinclaude_args. - Don't pass
GITHUB_TOKENasgithub_tokenif you want CI to run on Claude's commits. - Open a non-draft PR from a branch in the same repo and check for a comment.
- Add a
CLAUDE.mdwith your review criteria.
Quick answers
How do I run Claude Code in GitHub Actions?
Add the anthropics/claude-code-action@v1 step to a workflow, pass your key as anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}, and give it a prompt or let it respond to @claude. Running /install-github-app in claude sets this up for you.
What permissions does the Claude Code GitHub Action need?
The Action relies on the Claude GitHub App's Contents, Issues and Pull requests permissions (read and write). In the workflow, a review job can use read access to contents, pull requests and issues plus id-token: write.
Why is the Claude Code GitHub Action not working on my pull request?
Common causes are a missing --comment or --allowedTools, no id-token: write, a draft or already-reviewed PR, or a fork PR on a public repo where GitHub withholds secrets.
Can I use my Claude subscription instead of an API key?
Yes, on Pro, Max, Team and Enterprise plans. Run claude setup-token locally, store the result as CLAUDE_CODE_OAUTH_TOKEN, and pass it to the claude_code_oauth_token input.
Did this solve it?
One tap. No account.
Sorry about that. What was missing?
from 0 readers
Saved you an hour? Send a tip.
USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.
Network
0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
On a computer? Scan the code with your phone wallet, or copy the address.
Pick the network your wallet or exchange uses. USDC works on all six; ETH on Base, Ethereum, Arbitrum and Optimism. Don't send on Tron or Solana: this address doesn't exist there. Tips are optional and unlock nothing; every guide stays free.
Discussion
Start the discussion
Next note