anthropics / anthropics/claude-code-action
User-provided MCP config file paths are silently discarded when action prepends inline JSON
- 主要言語
- TypeScript
- スター
- 8.9k
- フォーク
- 2.1k
- 平均マージ
- 3日 9時間
- マージ済み PR(30日)
- 10
説明
## 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
```yaml
- 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:
```json
"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:
```typescript
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:
1. The action prepends inline JSON config for built-in servers
2. User provides a file path
3. File path is stored in `lastFilePath` but never read
4. Since inline JSON exists, the condition `Object.keys(merged.mcpServers!).length === 0` is false
5. 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:
1. Users have no way to include the action's built-in servers in their file (they don't know the paths)
2. The file is silently discarded with no warning
3. The documentation doesn't mention this limitation
## Suggested fix
Read and merge file paths at parse time:
```typescript
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:
```yaml
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_args` with `--mcp-config` pointing to a file path
コントリビューションガイド
評価
この issue はまだ評価されていません。