How to turn production errors into GitHub issues with the Claude Code GitHub Action
Originally published at runheed.com. Add Runheed to the Claude Code GitHub Action with an MCP config file and an agent key secret, then run it on a schedule. Each run lists new production errors, reads the stack trace,
Originally published at runheed.com.
Add Runheed to the Claude Code GitHub Action with an MCP config file and an agent key secret, then run it on a schedule. Each run lists new production errors, reads the stack trace, opens the files it points to in your checked-out repo and files one GitHub issue per new error with the likely cause and a proposed fix.
What you need
- The Claude Code GitHub Action set up in the repository, with an ANTHROPIC_API_KEY (or CLAUDE_CODE_OAUTH_TOKEN) secret. Running /install-github-app in Claude Code does this.
- A Runheed agent key (Settings > Agent keys) saved as a repository secret named RUNHEED_API_KEY.
- Your app already reporting errors to Runheed through the Node or Python SDK.
The workflow
Save this as .github/workflows/production-errors.yml. It runs on weekday mornings (UTC) and can be started by hand from the Actions tab. The first step writes the MCP config; Claude Code fills in ${RUNHEED_API_KEY} from the step environment, so the key never appears in the file or the logs.
.github/workflows/production-errors.yml
name: Production errors to issues
on:
schedule:
- cron: "17 13 * * 1-5"
workflow_dispatch:
jobs:
triage:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v6
- name: Runheed MCP config
run: |
cat > "$RUNNER_TEMP/runheed-mcp.json" <<'EOF'
{"mcpServers": {"runheed": {"type": "http", "url": "https://runheed.com/api/mcp",
"headers": {"Authorization": "Bearer ${RUNHEED_API_KEY}"}}}}
EOF
- uses: anthropics/claude-code-action@v1
env:
RUNHEED_API_KEY: ${{ secrets.RUNHEED_API_KEY }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
REPO: ${{ github.repository }}
1. Call runheed_search_errors with status unresolved, and runheed_get_recent.
Keep errors first seen in the last 24 hours, or whose count went up.
2. For each one, run gh issue list --search "<error id>" --state all.
Skip it if an issue already mentions that id.
3. Otherwise call runheed_get_error, open the files in its stack trace,
and create one issue with gh issue create:
title "[prod] <error type>: <message>", body with the Runheed error id,
count, first and last seen, file:line, the likely cause quoting the code,
and a proposed fix. Do not change any files.
claude_args: |
--mcp-config ${{ runner.temp }}/runheed-mcp.json
--max-turns 40
--allowedTools "mcp__runheed__runheed_search_errors,mcp__runheed__runheed_get_recent,mcp__runheed__runheed_get_error,mcp__runheed__runheed_get_error_context,Read,Glob,Grep,Bash(gh issue:*)"
The allowed tools are read-only on the Runheed side and in the repository: the run can read errors and code and create issues, nothing else. Searching by the Runheed error id before filing means an error that keeps happening gets one issue, not one per day.
Let it open a draft pull request too
- Change the job permissions to contents: write and pull-requests: write.
- Add Edit, Write, Bash(git:) and Bash(gh pr create:) to --allowedTools, plus your test command (for example Bash(npm test:*)).
- Replace "Do not change any files" with: "For the top error, write a failing test that reproduces it, fix the code on a new branch, run the tests and open a draft pull request that links the issue."
You review and merge the pull request as usual. After it ships, resolve the error with the commit hash (runheed_resolve_error); if it comes back, Runheed reopens it and the next run picks it up again.
Tested how
The MCP part of this setup (the config file with the key in an Authorization header, read from an environment variable, and the tool names in --allowedTools) was run with claude -p against https://runheed.com/api/mcp on 2026-11-04: Claude listed the unresolved errors and read the top one's stack trace. The workflow structure follows the scheduled examples in the Claude Code GitHub Actions docs and the action's own repository.
FAQ
Why not let the action use OAuth like Claude Code on my laptop?
A scheduled run has nobody to click Allow in a browser, so it uses an agent key in a header instead. Keys can be revoked in Settings at any time, and Claude Code never expands credential-style variables such as ANTHROPIC_API_KEY into a remote server header, but RUNHEED_API_KEY is passed as written.
How much does a run cost?
Runs use your Claude API key or subscription like any other Claude Code session. --max-turns caps the length of a run; a day with no new errors ends after a couple of tool calls.
Does it work with GitLab or other CI?
The Runheed part is the same anywhere Claude Code runs: write the MCP config, set RUNHEED_API_KEY and run claude -p with the same --allowedTools. Only the issue command changes.
Related
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.