Skip to content

Agent mode: MCP-server install decision uses a different parser (parseAllowedTools) than the one that builds the actual tool list, and the parsers diverge on comments and multi-line flag values #1357

Description

@KenSCompareCredit

Summary

In agent mode, the action decides which bundled MCP servers to install using one parser (parseAllowedTools, a regex scanner in src/modes/agent/parse-tools.ts), but builds the actual allowedTools the SDK receives using a different parser (parseSdkOptionsparseClaudeArgsToExtraArgs, a shell-quote-based parser in base-action/src/parse-sdk-options.ts).

These two parsers interpret the same --allowedTools / --disallowedTools content differently. As a result, the set of tools used to decide MCP-server provisioning can differ from the set actually granted to the model. When they disagree on an mcp__github* entry, the action either fails to start a server whose tools the model is nonetheless told it has, or starts a server based on a tool reference the model never actually receives.

I've confirmed 2 distinct divergences (repros below):

  1. Comment linesparseAllowedTools does not strip # comment lines, so a --allowedTools "…" pattern appearing inside a comment is counted. parseSdkOptions correctly ignores it (it calls stripShellComments). This is a direct consequence of the shell-quote treats # as comment, swallowing all subsequent flags in claude_args #802 fix being applied to only one of the two parsers.
  2. Multi-line single-flag values — when one --allowedTools is followed by multiple quoted values on subsequent lines, parseAllowedTools captures only the first value, while parseSdkOptions (whose accumulating-flag logic consumes all consecutive non-flag tokens) captures all of them.

Context on the two parsers

  • src/modes/agent/index.ts calls parseAllowedTools(userClaudeArgs) and passes the result to prepareMcpConfig(...). prepareMcpConfig (src/mcp/install-mcp-server.ts) uses it to decide whether to spin up MCP servers like github, github_inline_comment, github_ci, github_comment, etc. (e.g. hasGitHubMcpTools = allowedToolsList.some(t => t.startsWith("mcp__github__"))).
  • Separately, base-action/src/parse-sdk-options.ts (parseClaudeArgsToExtraArgs + the allowed/disallowed merge logic in parseSdkOptions) parses the same claude_args to build the allowedTools / disallowedTools actually handed to the Agent SDK.

Because the install decision and the SDK tool list come from two different parsers, any input shape the two parsers handle differently produces an inconsistent session: the tools may be in allowedTools but the backing MCP server is never started (or vice-versa).

Relationship to previously-fixed issues

A cluster of parseAllowedTools bugs has already been fixed:

The remaining problem is the structural one: there are still two independent parsers, and the #802 comment-stripping fix was applied to just one of them. Divergence 1 below is the comment-handling inconsistency that fix left behind; divergence 2 is a separate multi-line gap that the #800 fix (multiple flags) did not address (it concerns a single flag with multiple whitespace/newline-separated values, a shape shell-quote accumulates but the regex does not).

Reproduction

Run identical claude_args through both parsers and compare. (Imported directly from the action source; parseAllowedTools from src/modes/agent/parse-tools.ts, allowedTools extracted from parseSdkOptions in base-action/src/parse-sdk-options.ts.)

Divergence 1 — comment line containing a flag pattern

claude_args:
# example: --allowedTools "mcp__github__SHOULD_NOT_COUNT"
--allowedTools "Read"
  • parseAllowedTools (drives MCP-server install): ["mcp__github__SHOULD_NOT_COUNT", "Read"]
  • parseSdkOptions (actual SDK tools): ["Read"]

parseAllowedTools would conclude an mcp__github__* tool is requested and spin up the github MCP server, even though that tool is only mentioned in a comment and is never granted to the model.

Divergence 2 — multi-line values after a single --allowedTools

claude_args:
--allowedTools
  "Read"
  "Grep"
  "mcp__github__get_commit"
  • parseAllowedTools (drives MCP-server install): ["Read"]
  • parseSdkOptions (actual SDK tools): ["Read", "Grep", "mcp__github__get_commit"]

Here the inverse happens: the mcp__github__get_commit tool is granted to the model, but parseAllowedTools doesn't see it, so the github MCP server is not started — the model is told it has a tool whose server never spawned. (This is the failure mode that bit us; see "Real-world impact" below.)

Control cases (parsers agree)

  • Single-line comma-separated --allowedTools "Read,Grep,mcp__github__get_commit" → both ["Read","Grep","mcp__github__get_commit"].
  • Repeated single-value flags --allowedTools "Read" / --allowedTools "mcp__github__get_commit" → both agree.
  • Prose-only comment (no flag pattern) above a real flag → both agree.

Real-world impact

We hit divergence #2 in a PR-review workflow (agent mode). With a multi-line --allowedTools block, parseAllowedTools saw only the first tool, so the github/github_ci/github_inline_comment servers were never installed — even though those tools appeared in the model's allowedTools. Switching to single-line comma-separated values made parseAllowedTools see the GitHub tools and install the servers. (Switching formats then exposed #1004, the file-path --mcp-config drop — separate issue.) The net effect is that the formatting of --allowedTools silently determines whether MCP servers come up, with no error or warning.

Suggested fix

Have the MCP-server install decision consume the same parsed tool list that the SDK ultimately receives, rather than re-parsing claude_args with a separate regex. Concretely, derive prepareMcpConfig's allowedTools from the already-parsed/normalized result produced by the shell-quote path in parse-sdk-options.ts (which already strips comments and accumulates multi-value flags), instead of calling parseAllowedTools. That removes the structural possibility of divergence rather than patching each parser to match.

If keeping 2 parsers is preferred for now, synchronize their handling of comments and multi-line inputs.

Environment

  • anthropics/claude-code-action@v1
  • Mode: agent (explicit prompt provided)
  • Verified by importing the action's actual parser sources and running identical claude_args through both.

API Provider

[x] Anthropic First-Party API (default)
[ ] AWS Bedrock
[ ] GCP Vertex

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:permissionsbugSomething isn't workingmcpp2Non-showstopper bug or popular feature request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions