DEV Community

Cover image for Set up the Claude Code GitHub Action properly
Kay
Kay

Posted on Originally published at latchkey.dev

Set up the Claude Code GitHub Action properly

The Claude Code GitHub Action, anthropics/claude-code-action@v1, runs Claude Code inside an Actions job: with no prompt input it waits for an @claude mention, and with one it runs on whatever event starts the workflow. Set it up with id-token: write, the narrowest job permissions for the mode, and the Claude GitHub App rather than GITHUB_TOKEN, or CI will not run on the commits it pushes.

Flow from a GitHub event through the action's trigger, write-access and bot checks to a comment or a pushed branch
One run of claude-code-action: three checks before Claude starts, two independent authentications, and the identity of the push deciding whether CI runs.

The minimal @claude workflow is three steps and works on the first try, which is why most repositories stop there. The parts that bite later are the ones the minimal version leaves implicit: which identity the commits are pushed as, what the job token can do, and who besides your team can start a run that holds your API key.

The opposite direction, Claude driving your CI from a terminal, is running CI from Claude Code, which also shows the minimal workflow. What follows is the full setup, with every input and error string read from the action at v1.0.273 and from Anthropic's documentation on 2026-11-12.

Two modes, chosen by one input

The mode input is gone; the action detects the mode itself. Without prompt it is interactive: it looks for the trigger phrase, @claude by default, in an issue or pull request comment, a pull request review, or the body or title of a newly opened issue, then replies in a comment it keeps updating. A label_trigger (default claude) and an assignee_trigger start the same flow from a label or an assignment.

With prompt set it is automation: Claude runs on any event, including schedule, and by default its results go to the workflow run log rather than a comment. A plain-text prompt starts with no shell and no GitHub API access; you grant the tools it needs with --allowedTools in claude_args. The if: on the job is not decoration: it keeps a runner from starting on every comment, and the action checks the phrase again itself.

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
    timeout-minutes: 30
    permissions:
      contents: write
      pull-requests: write
      issues: write
      id-token: write
      actions: read
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 1
      - uses: anthropics/claude-code-action@8a29bc6f0ae11a363721054b6739feba6adabe57 # v1.0.273
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          additional_permissions: |
            actions: read
          claude_args: --max-turns 20
Enter fullscreen mode Exit fullscreen mode

actions: read appears twice on purpose. The job permission lets the token read runs; additional_permissions is what makes the action start its CI tools (mcp__github_ci__get_ci_status, mcp__github_ci__get_workflow_run_details, mcp__github_ci__download_job_log), per docs/configuration.md at v1.0.273.

The inputs that matter

action.yml at v1.0.273 declares 40 inputs. These are the ones that change what the run can do or who can start it.

Input Default What it controls
prompt empty Set: automation mode. Empty: wait for the trigger phrase
claude_args empty CLI flags: --model, --max-turns, --allowedTools, --permission-mode
anthropic_api_key / claude_code_oauth_token none Claude API key, or a subscription token from claude setup-token
anthropic_federation_rule_id + anthropic_organization_id none Workload identity federation: no stored Anthropic key
use_bedrock / use_vertex / use_foundry false Route inference through a cloud provider, authenticated by OIDC
github_token none Omitted: the action authenticates as the Claude GitHub App
allowed_bots empty (no bots) Bot actors allowed to trigger it; * allows all
allowed_non_write_users empty Users without write access; only works with github_token
restrict_untrusted_prs true Denies package managers, test runners and shells on pull requests from authors without write access
track_progress false Tracking comment on pull_request and issues events in automation mode

Defaults read from action.yml at tag v1.0.273 (commit 8a29bc6f0ae11a363721054b6739feba6adabe57) on 2026-11-12. The action also runs Claude Code in auto mode by default, where a safety review decides tool calls your allow rules do not cover; --permission-mode acceptEdits or default in claude_args restores a strict list.

Authentication is two separate questions

The first is how Claude reaches a model: an Anthropic API key, a Claude subscription OAuth token, workload identity federation against a Claude Console service account, or Amazon Bedrock, Google Cloud's Agent Platform (use_vertex) or Microsoft Foundry, each through OIDC. The second is how the action reaches GitHub: by default it requests an OIDC token, exchanges it with Anthropic for a Claude GitHub App installation token, and pushes and comments as claude[bot].

Both answers can need id-token: write, which is why the permission sits in every example. Leave it out on the default path and the run stops with "Could not fetch an OIDC token. Did you remember to add id-token: write to your workflow permissions?" A second message confuses people on the first pull request: when the workflow file itself is new or changed, the exchange refuses and the action logs that this is expected and that the workflow will begin working once you merge your PR.

With a cloud provider, Anthropic's documentation has you choose the GitHub identity yourself: the official app, a custom app you register with only Contents, Issues and Pull requests, or GITHUB_TOKEN. The official app is installed with its full permission set, including Actions and Workflows write, because GitHub does not let you accept a subset; a custom app is the way to hold the action to three permissions.

The identity also decides whether CI sees Claude's work. Branches pushed with GITHUB_TOKEN start no push run, so keep the App or a custom app token for any workflow that commits; running CI from Claude Code has the rule and the fix, and required checks for AI pull requests has what it takes for the resulting check to gate a merge.

```.github/workflows/claude.yml, Bedrock with a custom GitHub App
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- id: app-token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6.3.0
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: us-west-2
- uses: anthropics/claude-code-action@8a29bc6f0ae11a363721054b6739feba6adabe57 # v1.0.273
with:
github_token: ${{ steps.app-token.outputs.token }}
use_bedrock: "true"
claude_args: '--model us.anthropic.claude-sonnet-4-6'




> Anthropic's example uses `app-id`; `actions/create-github-app-token` v3.3.0 marks that input deprecated with "Use 'client-id' instead.", so the pin above uses the client ID.

## The minimum permissions for each job

Job permissions govern the job's own `GITHUB_TOKEN`, not the app token the action exchanges for. That is why Anthropic's code-review example posts inline comments from a job that holds only read scopes plus `id-token: write`: the comments are written with the Claude App's token. Grant write scopes only where Claude must commit, and only on the job that runs it.



```.github/workflows/claude-review.yml
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 1
      - uses: anthropics/claude-code-action@8a29bc6f0ae11a363721054b6739feba6adabe57 # v1.0.273
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          plugin_marketplaces: "https://github.com/anthropics/claude-plugins-official.git"
          plugins: "code-review@claude-plugins-official"
          prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
          claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
Enter fullscreen mode Exit fullscreen mode
Workflow Job permissions Why
@claude that may edit code contents: write, pull-requests: write, issues: write, id-token: write Commits, comments, App token exchange
Add CI access to the above actions: read plus additional_permissions Reads run status and job logs
Review on every pull request contents: read, pull-requests: read, issues: read, id-token: write Writes go through the App token
Scheduled report to the run log contents: read, issues: read, id-token: write No writes at all

Restricting who can start it

Before Claude starts, the action runs two checks on the actor. On issue and pull request events the user must have write access; a commenter without it gets Actor does not have write permissions to the repository. On every event, including schedule, a bot actor is refused with a message that starts Workflow initiated by non-human actor: and ends Add bot to allowed_bots list or use '*' to allow all bots. unless you list it.

Both escape hatches carry warnings in the action's own security document. Bots in allowed_bots are not checked for repository permissions at all, so on a public repository '*' lets any GitHub App that can comment drive Claude with a prompt it controls. allowed_non_write_users bypasses the write check, works only with github_token, and should only ever run with workflow permissions as small as issue labeling.

On pull requests whose author lacks write access, restrict_untrusted_prs (on by default) denies the commands that run code from the checkout, even if claude_args allows them; Claude can still read, edit and push. On public repositories, include_comments_by_actor narrows whose comments reach the prompt at all. What an outsider's text can do once it does reach Claude, and the two-job split that contains it, is on prompt injection in CI agents.

What stands behind this page

Source code and Anthropic's documentation, not a recorded run. Nothing in content/repro-evidence.mjs or content/heal-evidence.mjs covers this slug, so there is no captured log here and no repair claim. Every input, default and error message above was read from anthropics/claude-code-action at v1.0.273 (action.yml, docs/ and src/) and from code.claude.com on 2026-11-12; running the workflows would need an Anthropic key and an installed app, and the log they produce would show one account rather than the action's rules.

FAQ

Why is Claude not responding to @claude comments?

Check, in order: the Claude GitHub App is installed on the repository, the API key or OAuth token secret exists, the comment contains @claude as a whole word (not @claude-bot or /claude), and the commenter has write access. A comment from an account without write access fails the action's permission check before Claude starts.

Can I use my Claude Max subscription with the Claude Code GitHub Action?

Yes. Run claude setup-token locally, store the result as the CLAUDE_CODE_OAUTH_TOKEN secret, and pass it to the claude_code_oauth_token input instead of anthropic_api_key; Anthropic lists Pro, Max, Ultra, Team and Enterprise plans as eligible, and runs then use the subscription rather than API billing. For a secret shared across repositories, use an API key, because the token is tied to the subscription of whoever generated it.

Why am I getting OIDC authentication errors from claude-code-action?

The default GitHub App authentication requests an OIDC token, and GitHub only issues one when the job has id-token: write. When it is missing the action prints "Could not fetch an OIDC token. Did you remember to add id-token: write to your workflow permissions?" Bedrock, Agent Platform, Foundry and workload identity federation need the same permission.

Can I run the Claude Code GitHub Action with Bedrock or Vertex AI?

Yes. Set use_bedrock: "true" or use_vertex: "true" (Foundry is use_foundry), authenticate to the cloud first with its own OIDC action, grant id-token: write, and pass the provider's model ID through claude_args. Anthropic's examples pair this with a custom GitHub App token passed as github_token.

References

Top comments (0)