anthropics / anthropics/claude-code-action
Bundled MCP servers silently fail to spawn on pull_request events (mcp_servers empty despite correct config build)
- Dominant language
- TypeScript
- Stars
- 8.9k
- Forks
- 2.1k
- Avg merge
- 3d 9h
- Merged PRs (30d)
- 10
Description
## Summary
On `pull_request` events with `anthropics/claude-code-action@v1` (SHA `4e5d8b13ca281a6d163cdb287d8917b216e00d6f`), the action's bundled MCP servers — specifically `github_comment` and `github_inline_comment` — never spawn in the Claude CLI subprocess. Claude Code's session init message reports `"mcp_servers": []` on every run. The action correctly builds `--mcp-config '{…}'` via `src/mcp/install-mcp-server.ts` (verified by reading the source), but the spawned Claude CLI either doesn't receive or doesn't consume it. No startup error is logged — just silent absence.
This is reproducible across 10+ iterations with varying config (documented below).
## Environment
- Runner: GitHub-hosted `ubuntu-24.04`
- Bun 1.3.6 (installed by action)
- Claude CLI: `/home/runner/.local/bin/claude` (installed by action)
- Auth: `claude_code_oauth_token` (subscription OAuth)
- Event: `pull_request` (`synchronize`, same-repo branch, not a fork)
- Repo: private
## Reproduction
Minimal workflow that triggers the bug:
```yaml
jobs:
claude_review:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true
prompt: |
```
## Expected behavior
- Action detects tag mode (✅ confirmed in logs: `Auto-detected mode: tag for event: pull_request`)
- `prepareMcpConfig` builds JSON config containing `github_comment` server (✅ code path verified in `src/mcp/install-mcp-server.ts`)
- `tag/index.ts` appends `--mcp-config '{…}'` to `claudeArgs` (✅ confirmed)
- `parse-sdk-options.ts` keeps `mcp-config` in `extraArgs` for forwarding to the CLI (✅ confirmed)
- Claude CLI session init includes the server in `mcp_servers`
- Claude can call `mcp__github_comment__update_claude_comment` to update the tracking comment
## Actual behavior
- `Auto-detected mode: tag for event: pull_request` ✅
- SDK options `allowedTools` contains `mcp__github_comment__update_claude_comment` ✅
- Claude Code's init message:
```json
{
"type": "system",
"subtype": "init",
"tools": [ "Task", "Bash", "Edit", "Read", ... ],
"mcp_servers": []
}
```
- Claude attempts `mcp__github_comment__update_claude_comment` → `Error: No such tool available: mcp__github_comment__update_claude_comment`
- Claude falls back to trying `gh api` / `gh pr comment` — all denied (not in allowlist)
- Final sticky comment never updates from the action's `"I'll analyze this..."` placeholder
Full SDK output (`show_full_output: true`) confirms Claude repeatedly attempts the advertised MCP tool and gets `No such tool available`. The tool name is in `allowedTools` but the backing server isn't registered.
## What we tried (none fixed it)
- `track_progress: true` (forces tag mode — confirmed active)
- Explicit MCP tool names in `claude_args --allowed-tools`
- Adding `actions: read` permission (fixed unrelated `github_ci` warning; `github_comment` still missing)
- `settings: "{}"` override (made it worse — `permission_denials_count` went from 2 → 13)
- `claude_args: "--setting-sources user"` to bypass the project's `.claude/settings.json` (also made it worse)
- Custom prompt removed vs kept (no difference to MCP state)
- Various combinations of the above
In every configuration, `"mcp_servers": []` persists.
## Suspicion
The disconnect seems to sit between how the action builds `claudeArgs` and how `@anthropic-ai/claude-agent-sdk`'s `query()` forwards `extraArgs` to the spawned Claude CLI subprocess. The `--mcp-config` flag appears to be dropped or not consumed somewhere in that chain.
From `src/modes/tag/index.ts`:
```ts
const ourMcpConfig = await prepareMcpConfig({ …, mode: "tag", … });
claudeArgs = `--mcp-config '${escapedOurConfig}'`;
claudeArgs += ` --permission-mode acceptEdits --allowedTools "${tagModeTools.join(",")}"`;
if (userClaudeArgs) { claudeArgs += ` ${userClaudeArgs}`; }
```
From `base-action/src/parse-sdk-options.ts`:
```ts
// Pass through claudeArgs as extraArgs - CLI handles --mcp-config, --json-schema, etc.
extraArgs,
```
The code path is correct; the runtime outcome isn't.
## Workaround (for anyone finding this)
We shipped a wrapper script instead. Claude writes `/tmp/claude-review.json` via the `Write` tool (path-scoped), and a separate workflow step runs a bash script that validates the JSON and posts via `POST /repos/{owner}/{repo}/pulls/{n}/reviews`. Happy to share the workflow as a gist if useful.
## Asks
1. Can a maintainer confirm whether `--mcp-config` from `extraArgs` is supposed to reach the spawned Claude CLI when using `@anthropic-ai/claude-agent-sdk`'s `query()`?
2. If yes, any known environment where it silently drops (e.g., specific Bun version, specific settings file shape, OAuth vs API-key auth path)?
3. If no, what's the intended mechanism for the bundled MCP servers to be registered in the session?
Happy to provide full run logs (multiple, with `show_full_output: true` enabled) on request. I've redacted nothing that's sensitive but didn't want to paste hundreds of kB inline.
Contributor guide
Research direction
Start by reproducing the pull_request workflow with show_full_output enabled, then read src/mcp/install-mcp-server.ts, src/modes/tag/index.ts, and base-action/src/parse-sdk-options.ts. Trace whether --mcp-config reaches the Claude CLI through query() and compare the session init output. Done means the bundled server appears in mcp_servers and the advertised GitHub comment tool is available.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions, typescript
- Domain
- ci-cd, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100