Skip to content

GitHub Actions

Run Claude Code inside GitHub Actions with claude-code-action, from @claude mentions on issues and PRs to scheduled jobs and skill-driven reviews.

anthropics/claude-code-action is a GitHub Action that runs Claude Code inside your workflows. Tag @claude on an issue or pull request and it reads the code, makes changes and pushes commits. Give it a prompt instead and it runs unattended on any GitHub event: a cron schedule, a new PR, a release.

Several things share the Claude Code name, so to be clear about scope:

Setup

Either path needs admin access to the repository.

The quick way: /install-github-app

This only works for github.com remotes; on gitlab.com or bitbucket.org it prints a notice and exits (see GitLab CI/CD). Install the GitHub CLI and run gh auth login first.

In the repository, start claude and run:

/install-github-app

It then:

  1. Installs the Claude GitHub App. (You can choose Skip for now after this step and come back later.)
  2. Sets up a credential. If Claude Code already has an API key it reuses it, and offers to keep an existing ANTHROPIC_API_KEY secret. Otherwise you choose between a long-lived token from your Claude subscription or pasting an API key. The secret is saved as ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN.
  3. Pushes a branch containing the workflows you picked, wired to that secret, and opens GitHub with the PR ready to create.

Merge the PR and @claude works. Press Esc to stop partway; whatever step is running finishes, nothing after it starts, and the closing message lists what was already done.

If you choose the review workflow, Claude posts its review on the PR (inline comments per issue, or a single summary comment when it finds none) and skips some PRs such as drafts. Versions before v2.1.229 only wrote the review to the run log. To upgrade an older generated review workflow, either rerun /install-github-app and choose Update workflow file with latest version (new branch and PR), or add --comment and the claude_args line from the review example below to your existing file by hand.

Quick setup supports the Claude API and Claude subscriptions. For Bedrock, Agent Platform or Foundry, see GitHub Actions with cloud providers.

The manual way

  1. Install the Claude GitHub App on the repository. The action uses three of its permissions: Contents, Issues and Pull requests, all read and write.

  2. Add one secret:

    • ANTHROPIC_API_KEY: an API key from the Claude Console; or
    • CLAUDE_CODE_OAUTH_TOKEN: a subscription token (Pro, Max, Team, Enterprise) created locally with claude setup-token. See authentication.

    Pass it via the anthropic_api_key or claude_code_oauth_token input respectively.

  3. Add a workflow under .github/workflows/. The action's repository has a ready-made examples/claude.yml, or use the one below.

Test by commenting @claude on an issue.

Rolling out across an organisation

  • Install the app once at organisation level, for all or selected repos.
  • Store the credential as an organisation Actions secret. Use a Console API key here: an OAuth token is tied to whoever ran claude setup-token.
  • Add the workflow to each repo, or write it once as a reusable workflow and call it.

To avoid long-lived secrets altogether, use workload identity federation: the action trades the job's GitHub OIDC token for Claude API access via a Console service account. Set:

InputValue
anthropic_federation_rule_idThe rule ID, fdrl_...
anthropic_organization_idYour Anthropic organisation ID
anthropic_service_account_idOptional, svac_... (the rule already targets one)
anthropic_workspace_idOptional, wrkspc_... (needed only if the rule spans several workspaces)

and grant id-token: write, which the exchange needs even if you supply your own github_token. The Console-side steps are in the action repository's setup guide. For security reviews, see data usage and security.

Removing it

  • Delete workflows using anthropics/claude-code-action (quick setup created claude.yml and possibly claude-code-review.yml).
  • Delete the ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN secret at repo and org level. Deleting a secret does not revoke it; delete API keys in the Console too.
  • Uninstall the Claude GitHub App only if nothing else uses it (Code Review and web auto-fix do).
  • If you used a cloud provider, remove those secrets (AWS_ROLE_TO_ASSUME, GCP_*, AZURE_*) and any custom app with its APP_ID and APP_PRIVATE_KEY.

What the app is allowed to do

One Claude GitHub App serves the action, Code Review and cloud auto-fix, so its permission set is wider than the action needs:

PermissionAccess
ActionsRead and write
AdministrationRead
ChecksRead and write
ContentsRead and write
DiscussionsRead and write
IssuesRead and write
MembersRead
Merge queuesRead
MetadataRead
Pull requestsRead and write
Repository hooksRead and write
StatusesRead
WorkflowsRead and write

GitHub only lets you accept the whole set. When the app later asks for a new or wider permission (for example Actions moving from read to write so it can re-run workflows), the account or org owner must approve it, and the install keeps its old permissions until then. If policy demands least privilege, create your own GitHub App with just Contents, Issues and Pull requests (see the action's setup guide). A custom app only covers the action; Code Review and web auto-fix still need the official one.

How it decides what to do

ModeWhenOutput
InteractiveNo prompt input. Waits for the trigger phrase (@claude by default) in an issue or PR comment, a PR review, or a new issue's title or bodyA comment on the issue or PR, updated as it works
AutomationA prompt input is set. Runs on whatever event fires the workflowThe run log, unless the prompt tells Claude to post and it has a tool to do so

Who can trigger it

Two checks run before Claude starts; if either fails, the run fails:

  • Write access. On issue and PR events the actor needs write access. To allow particular users without it, set allowed_non_write_users and pass your own github_token. Events with no human author, like schedule, skip this.
  • Human actor. Bots are rejected unless listed in allowed_bots, which prevents loops. This applies to scheduled runs too, which GitHub attributes to whoever last edited the cron; if that was a bot, list it.

Workflows

The examples use an API key. With a subscription token, swap the anthropic_api_key line for claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}.

Answer @claude mentions

name: claude-on-mention
on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened]

jobs:
  respond:
    if: >
      contains(github.event.comment.body, '@claude') ||
      contains(github.event.issue.body, '@claude')
    runs-on: ubuntu-latest
    timeout-minutes: 30
    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 }}
          claude_args: "--max-turns 15"

Why the non-obvious bits are there:

  • id-token: write is needed for the action's default GitHub App authentication.
  • actions: read lets Claude read CI results on PRs.
  • actions/checkout gives Claude a working copy.
  • The if saves runner minutes on comments that do not mention Claude (the action double-checks anyway).

Then, on any issue or PR:

@claude the CSV export drops rows with a null region. Add a failing test, then fix it.
@claude why does this migration lock the orders table? Suggest a safer approach.

Run a skill as the prompt

prompt accepts a skill invocation:

  • For a skill in the repo's .claude/skills/, check the repo out first, then use /skill-name.
  • For a plugin skill, install it with plugin_marketplaces and plugins (format plugin-name@marketplace-name, where the marketplace name comes from its manifest, not its URL), then use /plugin-name:skill-name.

This review workflow installs the code-review plugin and runs it on PR activity, the same plugin quick setup uses:

name: pr-review
on:
  pull_request:
    types: [opened, synchronize, ready_for_review, reopened]

jobs:
  review:
    runs-on: ubuntu-latest
    concurrency:
      group: review-${{ github.event.pull_request.number }}
      cancel-in-progress: true
    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 decide where the review goes:

  • --comment makes Claude post on the PR (inline per issue, or one summary when clean). Without it, findings stay in the run log.
  • claude_args with --allowedTools must name the inline comment tool even though the skill's frontmatter already does, because the action only starts that MCP server when --allowedTools in claude_args mentions it.

Claude skips drafts, closed PRs, PRs it judges not worth reviewing (bots, trivial changes) and PRs it has already commented on. On public repos, GitHub withholds secrets from fork-triggered runs, so this only reviews same-repo branches. For zero-maintenance reviews, the managed Code Review is simpler.

Run on a schedule

With a plain-text prompt in automation mode, Claude has no shell or GitHub API access until you grant tools, via --allowedTools in claude_args or a permissions.allow rule in the settings input. A skill prompt can use whatever its allowed-tools frontmatter grants. GitHub runs schedules from the default branch only, and on public repos disables them after 60 days with no activity.

A weekly stale-issue sweep that reads and comments via GitHub MCP tools, so no checkout is needed:

name: weekly-issue-sweep
on:
  schedule:
    - cron: "30 7 * * 1"   # Mondays 07:30 UTC
  workflow_dispatch:

jobs:
  sweep:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    permissions:
      contents: read
      issues: write
      id-token: write
    steps:
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            List open issues with no activity in 30 days. For each, add a short comment
            asking the reporter whether it is still relevant. Skip anything labelled "pinned".
          claude_args: |
            --model claude-sonnet-5
            --max-turns 20
            --allowedTools "mcp__github__list_issues,mcp__github__add_issue_comment"

Good habits

Put house rules in CLAUDE.md. Style, review criteria and patterns in the repository root are read on every run. Keep it tight, since it is loaded every time. See memory.

Keep credentials in secrets.

Warning: Never commit an API key or OAuth token. Reference secrets, for example anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}.

Give each workflow the minimum permissions it needs and review Claude's changes before merging. The action's repository has its own security notes.

Watch costs. Each run spends GitHub Actions minutes on hosted runners and model tokens (or subscription usage with an OAuth token). To keep both down:

  • write specific requests so fewer turns are needed;
  • use issue templates to front-load context;
  • cap work with --max-turns;
  • set timeout-minutes on jobs;
  • use concurrency groups to stop pile-ups.

See costs, analytics and monitoring.

Cloud providers

By default the action calls the Claude API. To run inference in your own cloud account, set one of:

ProviderInput
Amazon Bedrockuse_bedrock: "true"
Google Cloud's Agent Platformuse_vertex: "true"
Microsoft Foundryuse_foundry: "true"

All three authenticate with OIDC federation, so no static cloud keys live in the repo. Full walkthroughs are in GitHub Actions with cloud providers.

Inputs reference

The commonly used with: inputs:

InputPurposeRequired
promptPlain text or a skill invocation. Omit for interactive modeNo
claude_argsAny Claude Code CLI argumentsNo
anthropic_api_keyClaude API keyFor the Claude API, unless you use an OAuth token or federation; not for cloud providers
claude_code_oauth_tokenSubscription token from claude setup-tokenNo
github_tokenToken for GitHub operations; defaults to the Claude GitHub AppNo
plugin_marketplacesNewline-separated marketplace Git URLsNo
pluginsNewline-separated plugins to installNo
settingsClaude Code settings as JSON or a path to a JSON fileNo
trigger_phraseDefaults to @claudeNo
use_bedrock / use_vertex / use_foundryRoute through a cloud providerNo

The full list is in the action repository's usage docs.

Useful claude_args:

ArgumentEffect
--max-turnsCap the number of turns
--modelChoose a model, e.g. claude-sonnet-5; otherwise the Claude Code default
--mcp-configPath to an MCP config
--allowedTools (or --allowed-tools)Comma-separated tools to allow
--debugVerbose debug output

Troubleshooting

@claude gets no response.

  • Is the app installed on this repo, and are Actions enabled?
  • Is the secret set?
  • Is it exactly @claude as a whole word (not /claude or @claude-bot)?
  • Does the commenter have write access (or appear in allowed_non_write_users)?

CI does not run on Claude's commits. GitHub does not trigger workflows for commits made with the default GITHUB_TOKEN. If you pass github_token: ${{ secrets.GITHUB_TOKEN }}, remove it so the action uses the Claude GitHub App, or supply a custom app token. Also check your CI triggers include push or pull_request.

Authentication errors. Test the key or token locally with claude first. For cloud providers, see the cloud provider troubleshooting.

The action repository also has a FAQ.

Upgrading from @beta

  1. Change anthropics/claude-code-action@beta to @v1.
  2. Delete the mode input; mode is detected automatically.
  3. Rename direct_prompt to prompt.
  4. Move CLI options such as max_turns and model into claude_args. custom_instructions becomes --append-system-prompt.