Skip to content

Commit 07a1cc3

Browse files
committed
Merge upstream/main into fork - resolve conflicts
- Kept OAuth authentication features in README.md quickstart section - Merged new claude_env input from upstream into action.yml - Maintained fork-specific claude-code-base-action reference
2 parents b5631ca + 8e8be41 commit 07a1cc3

19 files changed

+1269
-199
lines changed

.github/workflows/issue-triage.yml

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,20 @@ jobs:
2323
mkdir -p /tmp/mcp-config
2424
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
2525
{
26-
"github": {
27-
"command": "docker",
28-
"args": [
29-
"run",
30-
"-i",
31-
"--rm",
32-
"-e",
33-
"GITHUB_PERSONAL_ACCESS_TOKEN",
34-
"ghcr.io/github/github-mcp-server:sha-7aced2b"
35-
],
36-
"env": {
37-
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
26+
"mcpServers": {
27+
"github": {
28+
"command": "docker",
29+
"args": [
30+
"run",
31+
"-i",
32+
"--rm",
33+
"-e",
34+
"GITHUB_PERSONAL_ACCESS_TOKEN",
35+
"ghcr.io/github/github-mcp-server:sha-7aced2b"
36+
],
37+
"env": {
38+
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
39+
}
3840
}
3941
}
4042
}

README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ jobs:
104104
claude_expires_at: ${{ secrets.CLAUDE_EXPIRES_AT }}
105105

106106
timeout_minutes: "60"
107+
# Optional: add custom trigger phrase (default: @claude)
108+
# trigger_phrase: "/claude"
109+
# Optional: add assignee trigger for issues
110+
# assignee_trigger: "claude"
111+
# Optional: add custom environment variables (YAML format)
112+
# claude_env: |
113+
# NODE_ENV: test
114+
# DEBUG: true
115+
# API_URL: https://api.example.com
107116
```
108117

109118
## Inputs
@@ -125,13 +134,70 @@ jobs:
125134
| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" |
126135
| `disallowed_tools` | Tools that Claude should never use | No | "" |
127136
| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" |
137+
| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" |
128138
| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - |
129139
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
140+
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
130141

131142
\*Required when using direct Anthropic API (default and when not using Bedrock, Vertex, or OAuth)
132143

133144
> **Note**: This action is currently in beta. Features and APIs may change as we continue to improve the integration.
134145
146+
### Using Custom MCP Configuration
147+
148+
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.
149+
150+
#### Basic Example: Adding a Sequential Thinking Server
151+
152+
```yaml
153+
- uses: anthropics/claude-code-action@beta
154+
with:
155+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
156+
mcp_config: |
157+
{
158+
"mcpServers": {
159+
"sequential-thinking": {
160+
"command": "npx",
161+
"args": [
162+
"-y",
163+
"@modelcontextprotocol/server-sequential-thinking"
164+
]
165+
}
166+
}
167+
}
168+
allowed_tools: "mcp__sequential-thinking__sequentialthinking" # Important: Each MCP tool from your server must be listed here, comma-separated
169+
# ... other inputs
170+
```
171+
172+
#### Passing Secrets to MCP Servers
173+
174+
For MCP servers that require sensitive information like API keys or tokens, use GitHub Secrets in the environment variables:
175+
176+
```yaml
177+
- uses: anthropics/claude-code-action@beta
178+
with:
179+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
180+
mcp_config: |
181+
{
182+
"mcpServers": {
183+
"custom-api-server": {
184+
"command": "npx",
185+
"args": ["-y", "@example/api-server"],
186+
"env": {
187+
"API_KEY": "${{ secrets.CUSTOM_API_KEY }}",
188+
"BASE_URL": "https://api.example.com"
189+
}
190+
}
191+
}
192+
}
193+
# ... other inputs
194+
```
195+
196+
**Important**:
197+
198+
- Always use GitHub Secrets (`${{ secrets.SECRET_NAME }}`) for sensitive values like API keys, tokens, or passwords. Never hardcode secrets directly in the workflow file.
199+
- Your custom servers will override any built-in servers with the same name.
200+
135201
## Examples
136202

137203
### Ways to Tag @claude
@@ -276,6 +342,22 @@ This action is built on top of [`anthropics/claude-code-base-action`](https://gi
276342

277343
## Advanced Configuration
278344

345+
### Custom Environment Variables
346+
347+
You can pass custom environment variables to Claude Code execution using the `claude_env` input. This is useful for CI/test setups that require specific environment variables:
348+
349+
```yaml
350+
- uses: anthropics/claude-code-action@beta
351+
with:
352+
claude_env: |
353+
NODE_ENV: test
354+
CI: true
355+
DATABASE_URL: postgres://test:test@localhost:5432/test_db
356+
# ... other inputs
357+
```
358+
359+
The `claude_env` input accepts YAML format where each line defines a key-value pair. These environment variables will be available to Claude Code during execution, allowing it to run tests, build processes, or other commands that depend on specific environment configurations.
360+
279361
### Custom Tools
280362

281363
By default, Claude only has access to:

action.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ inputs:
3939
description: "Direct instruction for Claude (bypasses normal trigger detection)"
4040
required: false
4141
default: ""
42+
mcp_config:
43+
description: "Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers"
44+
claude_env:
45+
description: "Custom environment variables to pass to Claude Code execution (YAML format)"
46+
required: false
47+
default: ""
4248

4349
# Auth configuration
4450
anthropic_api_key:
@@ -93,21 +99,22 @@ runs:
9399
- name: Install Dependencies
94100
shell: bash
95101
run: |
96-
cd ${{ github.action_path }}
102+
cd ${GITHUB_ACTION_PATH}
97103
bun install
98104
99105
- name: Prepare action
100106
id: prepare
101107
shell: bash
102108
run: |
103-
bun run ${{ github.action_path }}/src/entrypoints/prepare.ts
109+
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/prepare.ts
104110
env:
105111
TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
106112
ASSIGNEE_TRIGGER: ${{ inputs.assignee_trigger }}
107113
BASE_BRANCH: ${{ inputs.base_branch }}
108114
ALLOWED_TOOLS: ${{ inputs.allowed_tools }}
109115
CUSTOM_INSTRUCTIONS: ${{ inputs.custom_instructions }}
110116
DIRECT_PROMPT: ${{ inputs.direct_prompt }}
117+
MCP_CONFIG: ${{ inputs.mcp_config }}
111118
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
112119
GITHUB_RUN_ID: ${{ github.run_id }}
113120

@@ -116,7 +123,7 @@ runs:
116123
if: steps.prepare.outputs.contains_trigger == 'true'
117124
uses: grll/claude-code-base-action@9f0fa5eeca1de34e9be1acf05f244e22e4527ca7 # Latest commit from fork
118125
with:
119-
prompt_file: /tmp/claude-prompts/claude-prompt.txt
126+
prompt_file: ${{ runner.temp }}/claude-prompts/claude-prompt.txt
120127
allowed_tools: ${{ env.ALLOWED_TOOLS }}
121128
disallowed_tools: ${{ env.DISALLOWED_TOOLS }}
122129
timeout_minutes: ${{ inputs.timeout_minutes }}
@@ -129,6 +136,7 @@ runs:
129136
claude_access_token: ${{ inputs.claude_access_token }}
130137
claude_refresh_token: ${{ inputs.claude_refresh_token }}
131138
claude_expires_at: ${{ inputs.claude_expires_at }}
139+
claude_env: ${{ inputs.claude_env }}
132140
env:
133141
# Model configuration
134142
ANTHROPIC_MODEL: ${{ inputs.model || inputs.anthropic_model }}
@@ -159,7 +167,7 @@ runs:
159167
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
160168
shell: bash
161169
run: |
162-
bun run ${{ github.action_path }}/src/entrypoints/update-comment-link.ts
170+
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/update-comment-link.ts
163171
env:
164172
REPOSITORY: ${{ github.repository }}
165173
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}

src/create-prompt/index.ts

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -31,53 +31,39 @@ const BASE_ALLOWED_TOOLS = [
3131
"Write",
3232
"mcp__github_file_ops__commit_files",
3333
"mcp__github_file_ops__delete_files",
34+
"mcp__github_file_ops__update_claude_comment",
3435
];
3536
const DISALLOWED_TOOLS = ["WebSearch", "WebFetch"];
3637

37-
export function buildAllowedToolsString(
38-
eventData: EventData,
39-
customAllowedTools?: string,
40-
): string {
38+
export function buildAllowedToolsString(customAllowedTools?: string[]): string {
4139
let baseTools = [...BASE_ALLOWED_TOOLS];
4240

43-
// Add the appropriate comment tool based on event type
44-
if (eventData.eventName === "pull_request_review_comment") {
45-
// For inline PR review comments, only use PR comment tool
46-
baseTools.push("mcp__github__update_pull_request_comment");
47-
} else {
48-
// For all other events (issue comments, PR reviews, issues), use issue comment tool
49-
baseTools.push("mcp__github__update_issue_comment");
50-
}
51-
5241
let allAllowedTools = baseTools.join(",");
53-
if (customAllowedTools) {
54-
allAllowedTools = `${allAllowedTools},${customAllowedTools}`;
42+
if (customAllowedTools && customAllowedTools.length > 0) {
43+
allAllowedTools = `${allAllowedTools},${customAllowedTools.join(",")}`;
5544
}
5645
return allAllowedTools;
5746
}
5847

5948
export function buildDisallowedToolsString(
60-
customDisallowedTools?: string,
61-
allowedTools?: string,
49+
customDisallowedTools?: string[],
50+
allowedTools?: string[],
6251
): string {
6352
let disallowedTools = [...DISALLOWED_TOOLS];
6453

6554
// If user has explicitly allowed some hardcoded disallowed tools, remove them from disallowed list
66-
if (allowedTools) {
67-
const allowedToolsArray = allowedTools
68-
.split(",")
69-
.map((tool) => tool.trim());
55+
if (allowedTools && allowedTools.length > 0) {
7056
disallowedTools = disallowedTools.filter(
71-
(tool) => !allowedToolsArray.includes(tool),
57+
(tool) => !allowedTools.includes(tool),
7258
);
7359
}
7460

7561
let allDisallowedTools = disallowedTools.join(",");
76-
if (customDisallowedTools) {
62+
if (customDisallowedTools && customDisallowedTools.length > 0) {
7763
if (allDisallowedTools) {
78-
allDisallowedTools = `${allDisallowedTools},${customDisallowedTools}`;
64+
allDisallowedTools = `${allDisallowedTools},${customDisallowedTools.join(",")}`;
7965
} else {
80-
allDisallowedTools = customDisallowedTools;
66+
allDisallowedTools = customDisallowedTools.join(",");
8167
}
8268
}
8369
return allDisallowedTools;
@@ -131,8 +117,10 @@ export function prepareContext(
131117
triggerPhrase,
132118
...(triggerUsername && { triggerUsername }),
133119
...(customInstructions && { customInstructions }),
134-
...(allowedTools && { allowedTools }),
135-
...(disallowedTools && { disallowedTools }),
120+
...(allowedTools.length > 0 && { allowedTools: allowedTools.join(",") }),
121+
...(disallowedTools.length > 0 && {
122+
disallowedTools: disallowedTools.join(","),
123+
}),
136124
...(directPrompt && { directPrompt }),
137125
...(claudeBranch && { claudeBranch }),
138126
};
@@ -447,33 +435,15 @@ ${sanitizeContent(context.directPrompt)}
447435
</direct_prompt>`
448436
: ""
449437
}
450-
${
451-
eventData.eventName === "pull_request_review_comment"
452-
? `<comment_tool_info>
453-
IMPORTANT: For this inline PR review comment, you have been provided with ONLY the mcp__github__update_pull_request_comment tool to update this specific review comment.
454-
455-
Tool usage example for mcp__github__update_pull_request_comment:
456-
{
457-
"owner": "${context.repository.split("/")[0]}",
458-
"repo": "${context.repository.split("/")[1]}",
459-
"commentId": ${eventData.commentId || context.claudeCommentId},
460-
"body": "Your comment text here"
461-
}
462-
All four parameters (owner, repo, commentId, body) are required.
463-
</comment_tool_info>`
464-
: `<comment_tool_info>
465-
IMPORTANT: For this event type, you have been provided with ONLY the mcp__github__update_issue_comment tool to update comments.
438+
${`<comment_tool_info>
439+
IMPORTANT: You have been provided with the mcp__github_file_ops__update_claude_comment tool to update your comment. This tool automatically handles both issue and PR comments.
466440
467-
Tool usage example for mcp__github__update_issue_comment:
441+
Tool usage example for mcp__github_file_ops__update_claude_comment:
468442
{
469-
"owner": "${context.repository.split("/")[0]}",
470-
"repo": "${context.repository.split("/")[1]}",
471-
"commentId": ${context.claudeCommentId},
472443
"body": "Your comment text here"
473444
}
474-
All four parameters (owner, repo, commentId, body) are required.
475-
</comment_tool_info>`
476-
}
445+
Only the body parameter is required - the tool automatically knows which comment to update.
446+
</comment_tool_info>`}
477447
478448
Your task is to analyze the context, understand the request, and provide helpful responses and/or implement code changes as needed.
479449
@@ -487,7 +457,7 @@ Follow these steps:
487457
1. Create a Todo List:
488458
- Use your GitHub comment to maintain a detailed task list based on the request.
489459
- Format todos as a checklist (- [ ] for incomplete, - [x] for complete).
490-
- Update the comment using ${eventData.eventName === "pull_request_review_comment" ? "mcp__github__update_pull_request_comment" : "mcp__github__update_issue_comment"} with each task completion.
460+
- Update the comment using mcp__github_file_ops__update_claude_comment with each task completion.
491461
492462
2. Gather Context:
493463
- Analyze the pre-fetched data provided above.
@@ -517,11 +487,11 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov
517487
- Look for bugs, security issues, performance problems, and other issues
518488
- Suggest improvements for readability and maintainability
519489
- Check for best practices and coding standards
520-
- Reference specific code sections with file paths and line numbers${eventData.isPR ? "\n - AFTER reading files and analyzing code, you MUST call mcp__github__update_issue_comment to post your review" : ""}
490+
- Reference specific code sections with file paths and line numbers${eventData.isPR ? "\n - AFTER reading files and analyzing code, you MUST call mcp__github_file_ops__update_claude_comment to post your review" : ""}
521491
- Formulate a concise, technical, and helpful response based on the context.
522492
- Reference specific code with inline formatting or code blocks.
523493
- Include relevant file paths and line numbers when applicable.
524-
- ${eventData.isPR ? "IMPORTANT: Submit your review feedback by updating the Claude comment. This will be displayed as your PR review." : "Remember that this feedback must be posted to the GitHub comment."}
494+
- ${eventData.isPR ? "IMPORTANT: Submit your review feedback by updating the Claude comment using mcp__github_file_ops__update_claude_comment. This will be displayed as your PR review." : "Remember that this feedback must be posted to the GitHub comment using mcp__github_file_ops__update_claude_comment."}
525495
526496
B. For Straightforward Changes:
527497
- Use file system tools to make the change locally.
@@ -576,8 +546,8 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov
576546
577547
Important Notes:
578548
- All communication must happen through GitHub PR comments.
579-
- Never create new comments. Only update the existing comment using ${eventData.eventName === "pull_request_review_comment" ? "mcp__github__update_pull_request_comment" : "mcp__github__update_issue_comment"} with comment_id: ${context.claudeCommentId}.
580-
- This includes ALL responses: code reviews, answers to questions, progress updates, and final results.${eventData.isPR ? "\n- PR CRITICAL: After reading files and forming your response, you MUST post it by calling mcp__github__update_issue_comment. Do NOT just respond with a normal response, the user will not see it." : ""}
549+
- Never create new comments. Only update the existing comment using mcp__github_file_ops__update_claude_comment.
550+
- This includes ALL responses: code reviews, answers to questions, progress updates, and final results.${eventData.isPR ? "\n- PR CRITICAL: After reading files and forming your response, you MUST post it by calling mcp__github_file_ops__update_claude_comment. Do NOT just respond with a normal response, the user will not see it." : ""}
581551
- You communicate exclusively by editing your single comment - not through any other means.
582552
- Use this spinner HTML when work is in progress: <img src="https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />
583553
${eventData.isPR && !eventData.claudeBranch ? `- Always push to the existing branch when triggered on a PR.` : `- IMPORTANT: You are already on the correct branch (${eventData.claudeBranch || "the created branch"}). Never create new branches when triggered on issues or closed/merged PRs.`}
@@ -650,7 +620,9 @@ export async function createPrompt(
650620
claudeBranch,
651621
);
652622

653-
await mkdir("/tmp/claude-prompts", { recursive: true });
623+
await mkdir(`${process.env.RUNNER_TEMP}/claude-prompts`, {
624+
recursive: true,
625+
});
654626

655627
// Generate the prompt
656628
const promptContent = generatePrompt(preparedContext, githubData);
@@ -661,16 +633,18 @@ export async function createPrompt(
661633
console.log("=======================");
662634

663635
// Write the prompt file
664-
await writeFile("/tmp/claude-prompts/claude-prompt.txt", promptContent);
636+
await writeFile(
637+
`${process.env.RUNNER_TEMP}/claude-prompts/claude-prompt.txt`,
638+
promptContent,
639+
);
665640

666641
// Set allowed tools
667642
const allAllowedTools = buildAllowedToolsString(
668-
preparedContext.eventData,
669-
preparedContext.allowedTools,
643+
context.inputs.allowedTools,
670644
);
671645
const allDisallowedTools = buildDisallowedToolsString(
672-
preparedContext.disallowedTools,
673-
preparedContext.allowedTools,
646+
context.inputs.disallowedTools,
647+
context.inputs.allowedTools,
674648
);
675649

676650
core.exportVariable("ALLOWED_TOOLS", allAllowedTools);

0 commit comments

Comments
 (0)