Skip to content

--mcp-config file path in claude_args is silently dropped when action's inline JSON config is present #1004

Description

@tshtark

Summary

When a user passes --mcp-config /path/to/file.json inside claude_args, the file path is silently dropped and the custom MCP servers never start. The agent appears to have the tools available (they show up in allowedTools) but the MCP server is absent from mcp_servers entirely — so the tools are uncallable.

Root Cause (traced to source)

In base-action/src/parse-sdk-options.ts, mergeMcpConfigs has two branches:

  • Inline JSON (starts with {) → parsed and merged into merged.mcpServers
  • File path → stored as lastFilePath, returned only if merged.mcpServers is empty

The action always prepends its own built-in servers (e.g. github_comment) as inline JSON before user claude_args are processed. This means merged.mcpServers is never empty by the time user's file path is evaluated. The condition on line 69:

if (Object.keys(merged.mcpServers!).length === 0 && lastFilePath) {
  return lastFilePath;
}

is never true, so the function falls through to return JSON.stringify(merged) — returning only the action's own inline-JSON servers, with the user's file path silently lost.

The code comment acknowledges this but proposes an impossible workaround:

"If user passes a file path, they should ensure it includes all needed servers."

This is not feasible since users cannot know the action's internal server configuration at workflow-write time, and it changes between action versions.

Note also that the function-level docstring contradicts the implementation:

"For file paths, they are kept as-is (user's file takes precedence and is used last)."

Evidence From CI Logs

Session init (extraArgs["mcp-config"]) shows only the action's built-in server — the user's file path is absent:

"mcp-config": "{\"mcpServers\":{\"github_comment\":{...}}}"

mcp_servers at connection time: custom server is not listed as FAILED or needs-auth — it is completely absent, confirming the config referencing it was never passed to the SDK.

The allowedTools array does include the custom tools (because --allowedTools is a separate flag that passes through verbatim), creating a misleading state: tools appear allowed but the server never started.

Steps to Reproduce

  1. Write a custom MCP config to a temp file in a workflow step:
    - name: Write MCP config
      run: |
        cat > /tmp/my-mcp-config.json <<'EOF'
        {"mcpServers":{"my_server":{"command":"uvx","args":["..."]}}}
        EOF
  2. Pass it via claude_args:
    - uses: anthropics/claude-code-action@v1
      with:
        claude_args: --mcp-config /tmp/my-mcp-config.json --allowedTools "mcp__my_server__*"
  3. Inspect session init logs — my_server will be absent from mcp_servers.

Expected Behavior

The function docstring says:

"For file paths, they are kept as-is (user's file takes precedence and is used last)."

Both the action's built-in inline JSON servers and the user's file-path server should be active in the session.

Workaround

Pass the custom MCP config as inline JSON instead of a file path. Inline JSON is merged correctly:

- uses: anthropics/claude-code-action@v1
  with:
    claude_args: >-
      --mcp-config '{"mcpServers":{"my_server":{"command":"uvx","args":["..."]}}}'
      --allowedTools "mcp__my_server__*"

The "Write MCP config" step can be removed entirely when using this approach.

Suggested Fix

Read the file at merge time — existsSync and readFileSync are already imported in run.ts and available in the runner environment. When a file path is encountered in mergeMcpConfigs, read and parse the file content and merge its mcpServers with the rest, rather than storing it as lastFilePath and conditionally returning it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething 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