-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Summary
When providing a --mcp-config flag with a file path in claude_args, the user's MCP servers are silently discarded because the action prepends its own inline JSON config. The mergeMcpConfigs function in base-action/src/parse-sdk-options.ts does not read file paths when inline JSON configs exist.
Reproduction
Workflow configuration
- name: Setup GitHub MCP Server Config
run: |
mkdir -p /tmp/mcp-config
cat > /tmp/mcp-config/mcp-servers.json << EOF
{
"mcpServers": {
"my_custom_server": {
"command": "docker",
"args": ["run", "-i", "--rm", "my-mcp-server"],
"env": {}
}
}
}
EOF
- name: Run Claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "Do something"
claude_args: |
--mcp-config /tmp/mcp-config/mcp-servers.json
--allowedTools "mcp__my_custom_server__some_tool"Expected behavior
The final MCP config should include both:
- The action's built-in servers (
github_comment,github_ci, etc.) - The user's custom server (
my_custom_server)
Actual behavior
Only the action's built-in servers are included. The user's file path is silently discarded, and my_custom_server is never available.
From the action logs:
"extraArgs": {
"mcp-config": "{\"mcpServers\":{\"github_comment\":{...},\"github_ci\":{...}}}",
}The user's my_custom_server is missing entirely.
Root cause
In base-action/src/parse-sdk-options.ts, the mergeMcpConfigs function handles file paths incorrectly:
function mergeMcpConfigs(configValues: string[]): string {
const merged: McpConfig = { mcpServers: {} };
let lastFilePath: string | null = null;
for (const config of configValues) {
const trimmed = config.trim();
if (!trimmed) continue;
if (trimmed.startsWith("{")) {
// Inline JSON - gets merged ✓
const parsed = JSON.parse(trimmed) as McpConfig;
Object.assign(merged.mcpServers!, parsed.mcpServers);
} else {
// File path - just stored, never read ✗
lastFilePath = trimmed;
}
}
// File path only returned if NO inline JSON exists
if (Object.keys(merged.mcpServers!).length === 0 && lastFilePath) {
return lastFilePath;
}
// File path is discarded here!
return JSON.stringify(merged);
}The issue is that:
- The action prepends inline JSON config for built-in servers
- User provides a file path
- File path is stored in
lastFilePathbut never read - Since inline JSON exists, the condition
Object.keys(merged.mcpServers!).length === 0is false - The function returns only the merged inline JSON, discarding the file path
The code comments acknowledge this limitation but describe it as expected behavior:
"If user passes a file path, they should ensure it includes all needed servers"
However, this is problematic because:
- Users have no way to include the action's built-in servers in their file (they don't know the paths)
- The file is silently discarded with no warning
- The documentation doesn't mention this limitation
Suggested fix
Read and merge file paths at parse time:
import { readFileSync } from "fs";
function mergeMcpConfigs(configValues: string[]): string {
const merged: McpConfig = { mcpServers: {} };
for (const config of configValues) {
const trimmed = config.trim();
if (!trimmed) continue;
let configToMerge: McpConfig;
if (trimmed.startsWith("{")) {
// Inline JSON
configToMerge = JSON.parse(trimmed);
} else {
// File path - read and parse
try {
const fileContent = readFileSync(trimmed, "utf-8");
configToMerge = JSON.parse(fileContent);
} catch (error) {
console.warn(`Failed to read MCP config file: ${trimmed}`, error);
continue;
}
}
if (configToMerge.mcpServers) {
Object.assign(merged.mcpServers!, configToMerge.mcpServers);
}
}
return JSON.stringify(merged);
}Workaround
Until this is fixed, users must provide their MCP config as inline JSON instead of a file path:
claude_args: |
--mcp-config '{"mcpServers":{"my_custom_server":{"command":"docker","args":["run","-i","--rm","my-mcp-server"],"env":{}}}}'This is cumbersome for complex configurations and doesn't match the documented behavior.
Environment
anthropics/claude-code-action@v1- Using
claude_argswith--mcp-configpointing to a file path