CoreMesh

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.

Claude Code GitHub Actions PR Review: Minimal Workflow

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 @claude in an issue or PR comment, and Claude analyzes code, implements changes, or pushes commits.
  • Automation mode: you give the action a prompt and 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:

text
/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.

yaml
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:

  • plugins and plugin_marketplaces: install the code-review plugin. The plugins value is plugin-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_args with --allowedTools: keep it. The action starts the MCP server that posts inline comments only when --allowedTools in claude_args names 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:

yaml
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:

yaml
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.

python
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:

yaml
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 --comment argument and the claude_args line from the example above.
  • Inline comments never post. --allowedTools isn't in claude_args. The skill's frontmatter alone doesn't start the MCP server.
  • The job fails at authentication. Check id-token: write is in the job's permissions, and that the secret name matches the input (anthropic_api_key vs claude_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 pass github_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 include push or pull_request.
  • /install-github-app exits immediately. Your remote isn't on github.com.
  • An old generated file behaves differently. Rerun /install-github-app and choose Update workflow file with latest version when a claude.yml already 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#

  1. Install the Claude GitHub App (or run /install-github-app).
  2. Add ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN as a repository secret.
  3. Commit the review workflow with read-only job permissions plus id-token: write.
  4. Keep --comment in prompt and --allowedTools in claude_args.
  5. Don't pass GITHUB_TOKEN as github_token if you want CI to run on Claude's commits.
  6. Open a non-draft PR from a branch in the same repo and check for a comment.
  7. Add a CLAUDE.md with 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.

Saved you an hour? Send a tip.

USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.

Base

Network

0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
Open in wallet · 5 USDC

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

`code` and ``` blocks work

No account needed. Comments are read before they show up, usually the same day. Your email is never asked for.

Indra Wijaya

Builds and runs AI agents, bots and content pipelines, and writes down what works and what broke.

About · Email · RSS

Keep reading

New notes, no spam.

One post at a time, only when there's something worth reading. Follow in any feed reader.

Subscribe via RSS