diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index 7f120ea..5705aa7 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -101,7 +101,8 @@ jobs: with: prompt_file: /tmp/claude-prompts/triage-prompt.txt allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues" - mcp_config: /tmp/mcp-config/mcp-servers.json + claude_args: | + --mcp-config /tmp/mcp-config/mcp-servers.json anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-mcp-servers.yml b/.github/workflows/test-mcp-servers.yml index 46db1a7..6c6432f 100644 --- a/.github/workflows/test-mcp-servers.yml +++ b/.github/workflows/test-mcp-servers.yml @@ -158,3 +158,134 @@ jobs: fi echo "✓ All MCP server checks passed with --mcp-config flag!" + + # Test the outer action with inline MCP config + test-outer-action-inline-mcp: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 #v2 + + - name: Install test MCP server dependencies + run: | + cd base-action/test/mcp-test + bun install + + - name: Test outer action with inline MCP config + uses: ./ + id: claude-inline-test + with: + prompt: "Use the mcp__test-server__test_tool to test the MCP integration" + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_args: | + --mcp-config '{"mcpServers":{"test-server":{"type":"stdio","command":"bun","args":["base-action/test/mcp-test/simple-mcp-server.ts"],"env":{}}}}' + --allowedTools mcp__test-server__test_tool + + - name: Check execution output + run: | + echo "Checking if MCP test tool was used..." + OUTPUT_FILE="${{ steps.claude-inline-test.outputs.execution_file }}" + + if [ ! -f "$OUTPUT_FILE" ]; then + echo "Error: Output file not found!" + exit 1 + fi + + cat $OUTPUT_FILE + + # Check if the tool was actually called - looking in assistant messages + if jq -e '.[] | select(.type == "assistant" and .message.content) | .message.content[] | select(.type == "tool_use" and .name == "mcp__test-server__test_tool")' "$OUTPUT_FILE" > /dev/null; then + echo "✓ MCP test tool was called" + + # Check the tool result - looking for the user message with tool_result + if jq -e '.[] | select(.type == "user" and .message.content[0].type == "tool_result") | .message.content[0].content[0].text | contains("Test tool response")' "$OUTPUT_FILE" > /dev/null; then + echo "✓ MCP test tool returned expected result" + else + echo "✗ MCP test tool result not as expected" + echo "Tool results in output:" + jq '.[] | select(.type == "user") | .message.content[]? | select(.type == "tool_result")' "$OUTPUT_FILE" + exit 1 + fi + else + echo "✗ MCP test tool was not called" + echo "Tools used:" + jq '.[] | select(.type == "assistant" and .message.content) | .message.content[] | select(.type == "tool_use") | .name' "$OUTPUT_FILE" + exit 1 + fi + + # Test the outer action with file-based MCP config + test-outer-action-file-mcp: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 #v2 + + - name: Install test MCP server dependencies + run: | + cd base-action/test/mcp-test + bun install + + - name: Create MCP config file + run: | + cat > /tmp/test-mcp-config.json << 'EOF' + { + "mcpServers": { + "test-server": { + "type": "stdio", + "command": "bun", + "args": ["base-action/test/mcp-test/simple-mcp-server.ts"], + "env": {} + } + } + } + EOF + + - name: Test outer action with file-based MCP config + uses: ./ + id: claude-file-test + with: + prompt: "Use the mcp__test-server__test_tool to test the MCP integration" + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_args: | + --mcp-config /tmp/test-mcp-config.json + --allowedTools mcp__test-server__test_tool + + - name: Check tool usage + run: | + echo "Checking if MCP test tool was used..." + OUTPUT_FILE="${{ steps.claude-file-test.outputs.execution_file }}" + + if [ ! -f "$OUTPUT_FILE" ]; then + echo "Error: Output file not found!" + exit 1 + fi + + cat $OUTPUT_FILE + + # Check if the tool was actually called - looking in assistant messages + if jq -e '.[] | select(.type == "assistant" and .message.content) | .message.content[] | select(.type == "tool_use" and .name == "mcp__test-server__test_tool")' "$OUTPUT_FILE" > /dev/null; then + echo "✓ MCP test tool was called" + + # Check the tool result - looking for the user message with tool_result + if jq -e '.[] | select(.type == "user" and .message.content[0].type == "tool_result") | .message.content[0].content[0].text | contains("Test tool response")' "$OUTPUT_FILE" > /dev/null; then + echo "✓ MCP test tool returned expected result" + else + echo "✗ MCP test tool result not as expected" + echo "Tool results in output:" + jq '.[] | select(.type == "user") | .message.content[]? | select(.type == "tool_result")' "$OUTPUT_FILE" + exit 1 + fi + else + echo "✗ MCP test tool was not called" + echo "Tools used:" + jq '.[] | select(.type == "tool") | .name' "$OUTPUT_FILE" + exit 1 + fi diff --git a/action.yml b/action.yml index 010155d..485737a 100644 --- a/action.yml +++ b/action.yml @@ -61,10 +61,6 @@ inputs: description: "Additional arguments to pass directly to Claude CLI" required: false default: "" - mcp_config: - description: "Additional MCP configuration (JSON string) that merges with built-in GitHub MCP servers" - required: false - default: "" additional_permissions: description: "Additional GitHub permissions to request (e.g., 'actions: read')" required: false @@ -146,7 +142,6 @@ runs: USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }} ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }} CLAUDE_ARGS: ${{ inputs.claude_args }} - MCP_CONFIG: ${{ inputs.mcp_config }} ALL_INPUTS: ${{ toJson(inputs) }} - name: Install Base Action Dependencies diff --git a/base-action/examples/issue-triage.yml b/base-action/examples/issue-triage.yml index 78a8caa..9ea0737 100644 --- a/base-action/examples/issue-triage.yml +++ b/base-action/examples/issue-triage.yml @@ -103,5 +103,6 @@ jobs: with: prompt_file: /tmp/claude-prompts/triage-prompt.txt allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues" - mcp_config: /tmp/mcp-config/mcp-servers.json + claude_args: | + --mcp-config /tmp/mcp-config/mcp-servers.json anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/docs/configuration.md b/docs/configuration.md index d85ea56..92a856b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,51 +2,47 @@ ## Using Custom MCP Configuration -The `mcp_config` input allows you to add custom MCP (Model Context Protocol) servers to extend Claude's capabilities. These servers merge with the built-in GitHub MCP servers. +You can add custom MCP (Model Context Protocol) servers to extend Claude's capabilities using the `--mcp-config` flag in `claude_args`. These servers merge with the built-in GitHub MCP servers. ### Basic Example: Adding a Sequential Thinking Server ```yaml -- uses: anthropics/claude-code-action@beta +- uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - mcp_config: | - { - "mcpServers": { - "sequential-thinking": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-sequential-thinking" - ] - } - } - } - allowed_tools: "mcp__sequential-thinking__sequentialthinking" # Important: Each MCP tool from your server must be listed here, comma-separated + claude_args: | + --mcp-config '{"mcpServers": {"sequential-thinking": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]}}}' + --allowedTools mcp__sequential-thinking__sequentialthinking # ... other inputs ``` ### Passing Secrets to MCP Servers -For MCP servers that require sensitive information like API keys or tokens, use GitHub Secrets in the environment variables: +For MCP servers that require sensitive information like API keys or tokens, you can create a configuration file with GitHub Secrets: ```yaml -- uses: anthropics/claude-code-action@beta - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - mcp_config: | - { - "mcpServers": { - "custom-api-server": { - "command": "npx", - "args": ["-y", "@example/api-server"], - "env": { - "API_KEY": "${{ secrets.CUSTOM_API_KEY }}", - "BASE_URL": "https://api.example.com" - } +- name: Create MCP Config + run: | + cat > /tmp/mcp-config.json << 'EOF' + { + "mcpServers": { + "custom-api-server": { + "command": "npx", + "args": ["-y", "@example/api-server"], + "env": { + "API_KEY": "${{ secrets.CUSTOM_API_KEY }}", + "BASE_URL": "https://api.example.com" } } } + } + EOF + +- uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_args: | + --mcp-config /tmp/mcp-config.json # ... other inputs ``` @@ -55,25 +51,31 @@ For MCP servers that require sensitive information like API keys or tokens, use For Python-based MCP servers managed with `uv`, you need to specify the directory containing your server: ```yaml -- uses: anthropics/claude-code-action@beta - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - mcp_config: | - { - "mcpServers": { - "my-python-server": { - "type": "stdio", - "command": "uv", - "args": [ - "--directory", - "${{ github.workspace }}/path/to/server/", - "run", - "server_file.py" - ] - } +- name: Create MCP Config for Python Server + run: | + cat > /tmp/mcp-config.json << 'EOF' + { + "mcpServers": { + "my-python-server": { + "type": "stdio", + "command": "uv", + "args": [ + "--directory", + "${{ github.workspace }}/path/to/server/", + "run", + "server_file.py" + ] } } - allowed_tools: "my-python-server__" # Replace with your server's tool names + } + EOF + +- uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_args: | + --mcp-config /tmp/mcp-config.json + --allowedTools my-python-server__ # Replace with your server's tool names # ... other inputs ``` @@ -84,10 +86,26 @@ For example, if your Python MCP server is at `mcp_servers/weather.py`, you would ["--directory", "${{ github.workspace }}/mcp_servers/", "run", "weather.py"] ``` +### Multiple MCP Servers + +You can add multiple MCP servers by using multiple `--mcp-config` flags: + +```yaml +- uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_args: | + --mcp-config /tmp/config1.json + --mcp-config /tmp/config2.json + --mcp-config '{"mcpServers": {"inline-server": {"command": "npx", "args": ["@example/server"]}}}' + # ... other inputs +``` + **Important**: - Always use GitHub Secrets (`${{ secrets.SECRET_NAME }}`) for sensitive values like API keys, tokens, or passwords. Never hardcode secrets directly in the workflow file. - Your custom servers will override any built-in servers with the same name. +- The `claude_args` supports multiple `--mcp-config` flags that will be merged together. ## Additional Permissions for CI/CD Integration @@ -322,5 +340,6 @@ Many individual input parameters have been consolidated into `claude_args` or `s | `model` | Use `claude_args: "--model claude-4-0-sonnet-20250805"` | | `claude_env` | Use `settings` with `"env"` object | | `custom_instructions` | Use `claude_args: "--system-prompt 'Your instructions'"` | +| `mcp_config` | Use `claude_args: "--mcp-config '{...}'"` | | `direct_prompt` | Use `prompt` input instead | | `override_prompt` | Use `prompt` with GitHub context variables | diff --git a/docs/migration-guide.md b/docs/migration-guide.md index fca63a7..91a806a 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -25,6 +25,7 @@ The following inputs have been deprecated and replaced: | `allowed_tools` | `claude_args: --allowedTools` | Use CLI format | | `disallowed_tools` | `claude_args: --disallowedTools` | Use CLI format | | `claude_env` | `settings` with env object | Use settings JSON | +| `mcp_config` | `claude_args: --mcp-config` | Pass MCP config via CLI arguments | ## Migration Examples @@ -156,17 +157,19 @@ claude_args: | --allowedTools Edit,Read,Write,Bash --disallowedTools WebSearch --system-prompt "You are a senior engineer focused on code quality" + --mcp-config '{"mcpServers": {"custom": {"command": "npx", "args": ["-y", "@example/server"]}}}' ``` ### Common claude_args Options -| Option | Description | Example | -| ------------------- | ------------------------ | ------------------------------------- | -| `--max-turns` | Limit conversation turns | `--max-turns 10` | -| `--model` | Specify Claude model | `--model claude-4-0-sonnet-20250805` | -| `--allowedTools` | Enable specific tools | `--allowedTools Edit,Read,Write` | -| `--disallowedTools` | Disable specific tools | `--disallowedTools WebSearch` | -| `--system-prompt` | Add system instructions | `--system-prompt "Focus on security"` | +| Option | Description | Example | +| ------------------- | ------------------------ | -------------------------------------- | +| `--max-turns` | Limit conversation turns | `--max-turns 10` | +| `--model` | Specify Claude model | `--model claude-4-0-sonnet-20250805` | +| `--allowedTools` | Enable specific tools | `--allowedTools Edit,Read,Write` | +| `--disallowedTools` | Disable specific tools | `--disallowedTools WebSearch` | +| `--system-prompt` | Add system instructions | `--system-prompt "Focus on security"` | +| `--mcp-config` | Add MCP server config | `--mcp-config '{"mcpServers": {...}}'` | ## Provider-Specific Updates @@ -190,6 +193,44 @@ claude_args: | --model claude-4-0-sonnet@20250805 ``` +## MCP Configuration Migration + +### Adding Custom MCP Servers + +**Before (v0.x):** + +```yaml +- uses: anthropics/claude-code-action@beta + with: + mcp_config: | + { + "mcpServers": { + "custom-server": { + "command": "npx", + "args": ["-y", "@example/server"] + } + } + } +``` + +**After (v1.0):** + +```yaml +- uses: anthropics/claude-code-action@v1 + with: + claude_args: | + --mcp-config '{"mcpServers": {"custom-server": {"command": "npx", "args": ["-y", "@example/server"]}}}' +``` + +You can also pass MCP configuration from a file: + +```yaml +- uses: anthropics/claude-code-action@v1 + with: + claude_args: | + --mcp-config /path/to/mcp-config.json +``` + ## Step-by-Step Migration Checklist - [ ] Update action version from `@beta` to `@v1` @@ -202,6 +243,7 @@ claude_args: | - [ ] Convert `allowed_tools` to `claude_args` with `--allowedTools` - [ ] Convert `disallowed_tools` to `claude_args` with `--disallowedTools` - [ ] Move `claude_env` to `settings` JSON format +- [ ] Move `mcp_config` to `claude_args` with `--mcp-config` - [ ] Test workflow in a non-production environment ## Getting Help diff --git a/src/entrypoints/collect-inputs.ts b/src/entrypoints/collect-inputs.ts index 501a438..bfb4008 100644 --- a/src/entrypoints/collect-inputs.ts +++ b/src/entrypoints/collect-inputs.ts @@ -17,7 +17,6 @@ export function collectActionInputsPresence(): void { custom_instructions: "", direct_prompt: "", override_prompt: "", - mcp_config: "", additional_permissions: "", claude_env: "", settings: "", diff --git a/src/modes/agent/index.ts b/src/modes/agent/index.ts index 43432b5..25191f1 100644 --- a/src/modes/agent/index.ts +++ b/src/modes/agent/index.ts @@ -119,13 +119,6 @@ export const agentMode: Mode = { claudeArgs = `--mcp-config '${escapedOurConfig}'`; } - // Add user's MCP_CONFIG env var as separate --mcp-config - const userMcpConfig = process.env.MCP_CONFIG; - if (userMcpConfig?.trim()) { - const escapedUserConfig = userMcpConfig.replace(/'/g, "'\\''"); - claudeArgs = `${claudeArgs} --mcp-config '${escapedUserConfig}'`.trim(); - } - // Append user's claude_args (which may have more --mcp-config flags) claudeArgs = `${claudeArgs} ${userClaudeArgs}`.trim(); diff --git a/src/modes/tag/index.ts b/src/modes/tag/index.ts index 6e380b7..5fe917b 100644 --- a/src/modes/tag/index.ts +++ b/src/modes/tag/index.ts @@ -155,13 +155,6 @@ export const tagMode: Mode = { const escapedOurConfig = ourMcpConfig.replace(/'/g, "'\\''"); claudeArgs = `--mcp-config '${escapedOurConfig}'`; - // Add user's MCP_CONFIG env var as separate --mcp-config - const userMcpConfig = process.env.MCP_CONFIG; - if (userMcpConfig?.trim()) { - const escapedUserConfig = userMcpConfig.replace(/'/g, "'\\''"); - claudeArgs = `${claudeArgs} --mcp-config '${escapedUserConfig}'`; - } - // Add required tools for tag mode claudeArgs += ` --allowedTools "${tagModeTools.join(",")}"`;