Skip to content

Claude Code and GitHub: Actions, PR Review, and CI

Mention @claude and it implements changes. Give it a prompt and it runs on every pull request without anyone asking. The second is where the value is.

Claude Code Guides: Claude Code and GitHub. A pipeline of push, review and merge stages with an @claude gate.

What the GitHub Action actually does

anthropics/claude-code-action runs Claude Code inside your repository's workflows. Two distinct uses come out of that:

Mention @claude in an issue or pull request comment and it analyses code, implements changes, and pushes commits — replying in a comment on the same thread and updating it as it works.

Give it a prompt and it runs automatically on any GitHub event: on every pull request, on a cron schedule, on a label being added. No mention needed.

The second is where most of the durable value is. A review that runs on every PR without anyone remembering to ask is worth more than a helpful assistant you have to summon.

Four things share the name — pick the right one

Worth two minutes now, because choosing wrong means building a workflow you did not need.

If you want Use
@claude in comments, and custom automation you control Claude Code GitHub Actions — this article
Automatic review on every PR, with no workflow file to maintain Code Review, a separate product
Claude Code sessions from a browser or phone Claude Code on the web
Automation outside GitHub entirely Claude Agent SDK — the Action is built on it

If all you want is PR review and you do not care how it is configured, use Code Review and stop reading. Everything below is for people who want control of the prompt, the model, the triggers, and the tools.

Setting it up

Two routes. Both need admin access to the repository.

Quick setup

Install the GitHub CLI and run gh auth login first — Claude Code checks for it and warns if it is missing. Then, in the repository:

claude
/install-github-app

That installs the Claude GitHub App, stores an authentication secret, pushes a branch with the workflow files you select, and opens a pull request ready to create. Merge it and @claude works.

The secret is named ANTHROPIC_API_KEY for an API key, or CLAUDE_CODE_OAUTH_TOKEN for a subscription token.

Manual setup

Use this if you do not run Claude Code locally, if the command fails, or if you want the workflow files under your own control.

  1. Install the Claude GitHub App. The Action relies on three of its permissions: Contents, Issues, and Pull requests, all read and write.
  2. Add a secret. Either ANTHROPIC_API_KEY from the Console, or CLAUDE_CODE_OAUTH_TOKEN generated locally with claude setup-token (available on Pro, Max, Team, and Enterprise).
  3. Copy a workflow file into .github/workflows/. The examples in the action's repository are working workflows, not sketches.

One note on the app's permissions that catches security teams: the Claude GitHub App is shared by every Claude feature that integrates with GitHub, so its permission set is broader than this Action uses — it includes Actions, Checks, Discussions, Members, Statuses and more. GitHub does not let you accept a subset. If your organisation will only grant what is actually needed, create a custom GitHub App with Contents, Issues, and Pull requests. A custom app covers this Action only; Code Review and web auto-fix still require the official one.

Interactive mode and automation mode

You do not choose the mode. The Action infers it from one thing: whether you supplied a prompt input.

Interactive Automation
Triggered by @claude in a comment, review, or new issue Any GitHub event
prompt input Absent Present
Where output goes A comment on the issue or PR The workflow run log, unless the prompt says otherwise

That last row is the one that confuses people. In automation mode Claude writes to the run log by default. If you want the output on the pull request, the prompt has to say so and Claude needs a tool that can post.

Two columns. Interactive mode has no prompt input, is triggered by @claude in a comment, review or new issue, and outputs a comment on the issue or PR. Automation mode has a prompt input, is triggered by any GitHub event including a cron schedule, and outputs to the run log unless the prompt says otherwise.
You do not pick the mode. One input decides it.

Workflow 1: respond to @claude

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 }}

Four lines in there are doing real work and are easy to omit:

  • id-token: write — required for the Action's default GitHub App authentication.
  • actions: read — lets Claude read CI results on the PR. Without it, it cannot see why your build failed.
  • actions/checkout — gives Claude a local copy to work in.
  • The if: condition — stops a runner starting on every comment. The Action checks the trigger phrase itself too, but by then you have already paid for the runner.

Then in any issue or PR:

@claude fix the TypeError in the user dashboard component
@claude how should I implement authorisation for this endpoint?

The trigger phrase is configurable via trigger_phrase. It must appear as a complete word — /claude and @claude-bot do not match.

Workflow 2: review every pull request

The higher-value workflow, because it runs whether or not anyone remembers.

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"'

Two lines control where the review ends up, and both are non-obvious:

--comment makes Claude post the review on the pull request — inline on each issue it finds, or one summary comment when it finds none. Leave it out and the findings sit in the run log where nobody reads them.

claude_args with --allowedTools must name the inline-comment tool even though the skill's own allowed-tools frontmatter already does. The Action starts the MCP server that posts inline comments only when --allowedTools in claude_args names it. Omit this and the review runs, finds things, and silently posts nothing.

Note also that prompt accepts a skill invocation, not just plain text. For a skill in your own repository, run actions/checkout first and pass /skill-name. For one in a plugin, install it via plugin_marketplaces and plugins, then pass the namespaced /plugin-name:skill-name.

Claude skips draft and closed pull requests, ones it judges trivial or automated, and ones that already carry a comment from it. On public repositories GitHub withholds secrets from fork pull requests, so reviews run only on same-repository branches.

Workflow 3: run on a schedule

name: Daily Report
on:
  schedule:
    - cron: "0 9 * * *"
jobs:
  report:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: read
      id-token: write
    steps:
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: "Generate a summary of yesterday's commits and open issues"
          claude_args: |
            --model claude-opus-4-8
            --allowedTools "mcp__github__list_commits,mcp__github__list_issues"

Notice there is no checkout step. Claude reads commits and issues through the GitHub API using the two MCP tools granted in claude_args, so it never needs the files.

The critical detail for scheduled runs: with a plain-text prompt, Claude has no shell or GitHub API access until you grant it. Either --allowedTools in claude_args, or a permissions.allow rule in the settings input. Invoke a skill instead and it gets whatever its allowed-tools frontmatter grants.

Two GitHub behaviours to plan around: scheduled workflows run only from the default branch, and on public repositories GitHub disables the schedule after 60 days without repository activity.

Who can trigger a run

Before Claude starts, the Action runs two checks on the triggering actor. Either one failing fails the run.

Write access. On issue and pull request events the user must have write access to the repository. To allow specific users without it, set allowed_non_write_users and pass your own github_token. Events with no author — a schedule trigger — skip this check.

Human actor. Bot actors are rejected unless listed in allowed_bots. This exists to stop bots triggering Claude in a loop, and it catches a case people do not expect: GitHub attributes scheduled runs to a repository user, usually whoever last changed the cron line. If that account is a bot, your schedule silently never runs until you list it.

Credentials, and the org-wide case

Never commit a key. Store it as a GitHub Secret and reference it as ${{ secrets.ANTHROPIC_API_KEY }}. That is the whole rule for a single repository.

Across an organisation:

  • Install the GitHub App once at the organisation level.
  • Store the secret as an organisation-level Actions secret so each repository does not need a copy.
  • Use an API key rather than an OAuth token for a shared secret — an OAuth token is tied to the subscription of whoever ran claude setup-token.
  • Define the job once as a reusable workflow that each repository calls, rather than copying YAML into dozens of repositories.

To avoid a long-lived secret entirely, authenticate through workload identity federation: the Action exchanges the workflow's GitHub OIDC token for Claude API access via a Console service account. Set anthropic_federation_rule_id and anthropic_organization_id, and grant the workflow id-token: write — which is needed for the federation exchange even when you pass your own github_token.

The wider deployment picture, including routing through Bedrock, Google Cloud's Agent Platform or Microsoft Foundry with use_bedrock, use_vertex and use_foundry, is covered in Claude Code for teams and enterprise.

Keeping the bill down

Every run spends two things: GitHub Actions minutes on the runner, and tokens.

The levers, in rough order of effect:

  • --max-turns in claude_args — a hard cap on iterations. The single most effective control.
  • Workflow-level timeouts, so a stuck job cannot burn an hour of runner time.
  • Concurrency controls to limit parallel runs on a busy repository.
  • A concise CLAUDE.md — it is read on every run, so every unnecessary line is billed repeatedly. This is the same argument for keeping it short that applies locally, with a meter attached.
  • Specific requests. A vague @claude mention costs more because it takes more turns to work out what you meant.

If you authenticate with an OAuth token, runs draw on your Claude subscription rather than API billing.

The four failures worth knowing in advance

Claude does not respond to @claude

Check, in order: the GitHub App is installed on this repository; workflows are enabled; the secret is set; the comment contains @claude as a complete word; and the commenting user has write access.

CI does not run on Claude's commits

GitHub does not trigger workflows on commits made with the default GITHUB_TOKEN. If you passed github_token: ${{ secrets.GITHUB_TOKEN }}, remove it so the Action authenticates as the Claude GitHub App instead — or pass a custom app token. This one looks like a broken CI configuration and is not.

The review runs but posts nothing

Almost always the missing --comment flag or the missing --allowedTools entry. Check the run log; the findings will be sitting in it.

Your old workflow stopped working

If it references @beta, migrate: change @beta to @v1, remove the mode input (the Action detects it now), rename direct_prompt to prompt, and move options like max_turns and model into claude_args.

Where to go next

The review this Action runs is a skill, usually delivered by a plugin — those two guides cover what to put in yours. For enforcing standards locally rather than in CI, see Claude Code hooks, and for the project context every run inherits, a production CLAUDE.md. The prompts worth automating are in the prompt library, and where CI fits in a full build is in the complete website workflow.

Sources and further reading

More Claude Code guides

Free download

The CLAUDE.md Starter Kit, free

Four working CLAUDE.md files you can drop into a project today, plus the one-page checklist for what belongs in one and how to tell whether yours is actually working.

  • CLAUDE.md for a static marketing site
  • CLAUDE.md for a web application, with security and migration rules
  • CLAUDE.md for a Shopify theme, including the gotchas that cost hours
  • CLAUDE.md for a shared package in a monorepo
  • A one-page checklist, and how to test the file is actually working

The download appears here as soon as you submit. I will also email you when there is a new guide worth reading. No fixed schedule, no selling your address, unsubscribe from any email. See the privacy policy.