Compare commits

..

8 Commits

Author SHA1 Message Date
Ashwin Bhat
2f766f1151 add issue triage workflow 2025-05-27 13:22:05 -07:00
Ashwin Bhat
253f2c6796 Pin GitHub Action dependencies to commit SHAs for security (#66)
Pin oven-sh/setup-bun and anthropics/claude-code-base-action to specific commit SHAs instead of version tags to ensure reproducible builds and improve supply chain security.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-05-27 10:14:11 -07:00
Ashwin Bhat
3c6a85b54b Improve error messages for GitHub Action authentication failures (#50)
- Add helpful hint about workflow permissions when OIDC token is not found
- Include response body in app token exchange failure errors for better debugging

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-05-25 18:43:54 -07:00
Lina Tawfik
cbc3ca285d Merge pull request #39 from anthropics/fix-mcp-undefined-error
Fix MCP file operations server errors
2025-05-23 12:30:01 -07:00
Lina Tawfik
6ce69a1db5 Remove test files to fix typecheck 2025-05-23 11:32:15 -07:00
Lina Tawfik
5b025a2e43 Fix prettier formatting 2025-05-23 11:31:08 -07:00
Lina Tawfik
a29981fe38 Remove inline comments from code 2025-05-23 11:22:47 -07:00
Lina Tawfik
c60a8fb69b Fix MCP server undefined error and file path resolution
- Add error field to MCP error responses to fix 'undefined' errors
- Add REPO_DIR environment variable to fix file path resolution
- Use GITHUB_WORKSPACE for correct repository directory
- Simplify path processing logic in commit_files tool

This fixes the issue where mcp__github_file_ops__commit_files would fail
with 'Error calling tool commit_files: undefined' by ensuring error messages
are properly formatted and files are read from the correct directory.
2025-05-23 11:17:05 -07:00
7 changed files with 160 additions and 29 deletions

View File

@@ -31,6 +31,6 @@ jobs:
- name: Run Claude Code
id: claude
uses: ./
uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

104
.github/workflows/issue-triage.yml vendored Normal file
View File

@@ -0,0 +1,104 @@
name: Claude Issue Triage
description: Run Claude Code for issue triage in GitHub Actions
on:
issues:
types: [opened]
jobs:
triage-issue:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup GitHub MCP Server
run: |
mkdir -p /tmp/mcp-config
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
{
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server:sha-7aced2b"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
}
}
}
EOF
- name: Create triage prompt
run: |
mkdir -p /tmp/claude-prompts
cat > /tmp/claude-prompts/triage-prompt.txt << 'EOF'
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
TASK OVERVIEW:
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
2. Next, use the GitHub tools to get context about the issue:
- You have access to these tools:
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
- Start by using mcp__github__get_issue to get the issue details
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
- Consider platform labels (android, ios) if applicable
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
5. Apply the selected labels:
- Use mcp__github__update_issue to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list above
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using mcp__github__update_issue
- It's okay to not add any labels if none are clearly applicable
EOF
- name: Run Claude Code for Issue Triage
uses: anthropics/claude-code-base-action@beta
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_file: /tmp/mcp-config/mcp-servers.json
timeout_minutes: "5"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

View File

@@ -67,7 +67,7 @@ runs:
using: "composite"
steps:
- name: Install Bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # https://github.com/oven-sh/setup-bun/releases/tag/v2.0.2
with:
bun-version: 1.2.11
@@ -94,7 +94,7 @@ runs:
- name: Run Claude Code
id: claude-code
if: steps.prepare.outputs.contains_trigger == 'true'
uses: anthropics/claude-code-base-action@beta
uses: anthropics/claude-code-base-action@5097b6cdfe5fc5a3ac0166cc344c34ed23c93982 # https://github.com/anthropics/claude-code-base-action/releases/tag/v0.0.5
with:
prompt_file: /tmp/claude-prompts/claude-prompt.txt
allowed_tools: ${{ env.ALLOWED_TOOLS }}

View File

@@ -434,9 +434,27 @@ ${
eventData.eventName === "pull_request_review_comment"
? `<comment_tool_info>
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.
Tool usage example for mcp__github__update_pull_request_comment:
{
"owner": "${context.repository.split("/")[0]}",
"repo": "${context.repository.split("/")[1]}",
"commentId": ${eventData.commentId || context.claudeCommentId},
"body": "Your comment text here"
}
All four parameters (owner, repo, commentId, body) are required.
</comment_tool_info>`
: `<comment_tool_info>
IMPORTANT: For this event type, you have been provided with ONLY the mcp__github__update_issue_comment tool to update comments.
Tool usage example for mcp__github__update_issue_comment:
{
"owner": "${context.repository.split("/")[0]}",
"repo": "${context.repository.split("/")[1]}",
"commentId": ${context.claudeCommentId},
"body": "Your comment text here"
}
All four parameters (owner, repo, commentId, body) are required.
</comment_tool_info>`
}
@@ -547,6 +565,9 @@ Important Notes:
- 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;" />
${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.`}
- Use mcp__github_file_ops__commit_files for making commits (works for both new and existing files, single or multiple). Use mcp__github_file_ops__delete_files for deleting files (supports deleting single or multiple files atomically), or mcp__github__delete_file for deleting a single file. Edit files locally, and the tool will read the content from the same path on disk.
Tool usage examples:
- mcp__github_file_ops__commit_files: {"files": ["path/to/file1.js", "path/to/file2.py"], "message": "feat: add new feature"}
- mcp__github_file_ops__delete_files: {"files": ["path/to/old.js"], "message": "chore: remove deprecated file"}
- Display the todo list as a checklist in the GitHub comment and mark things off as you go.
- REPOSITORY SETUP INSTRUCTIONS: The repository's CLAUDE.md file(s) contain critical repo-specific setup instructions, development guidelines, and preferences. Always read and follow these files, particularly the root CLAUDE.md, as they provide essential context for working with the codebase effectively.
- Use h3 headers (###) for section titles in your comments, not h1 headers (#).

View File

@@ -39,25 +39,19 @@ async function retryWithBackoff<T>(
}
}
throw new Error(
`Operation failed after ${maxAttempts} attempts. Last error: ${
lastError?.message ?? "Unknown error"
}`,
);
console.error(`Operation failed after ${maxAttempts} attempts`);
throw lastError;
}
async function getOidcToken(): Promise<string> {
try {
const oidcToken = await core.getIDToken("claude-code-github-action");
if (!oidcToken) {
throw new Error("OIDC token not found");
}
return oidcToken;
} catch (error) {
console.error("Failed to get OIDC token:", error);
throw new Error(
`Failed to get OIDC token: ${error instanceof Error ? error.message : String(error)}`,
"Could not fetch an OIDC token. Did you remember to add `id-token: write` to your workflow permissions?",
);
}
}
@@ -74,9 +68,15 @@ async function exchangeForAppToken(oidcToken: string): Promise<string> {
);
if (!response.ok) {
throw new Error(
`App token exchange failed: ${response.status} ${response.statusText}`,
const responseJson = (await response.json()) as {
error?: {
message?: string;
};
};
console.error(
`App token exchange failed: ${response.status} ${response.statusText} - ${responseJson?.error?.message ?? "Unknown error"}`,
);
throw new Error(`${responseJson?.error?.message ?? "Unknown error"}`);
}
const appTokenData = (await response.json()) as {
@@ -117,7 +117,9 @@ export async function setupGitHubToken(): Promise<string> {
core.setOutput("GITHUB_TOKEN", appToken);
return appToken;
} catch (error) {
core.setFailed(`Failed to setup GitHub token: ${error}`);
core.setFailed(
`Failed to setup GitHub token: ${error}.\n\nIf you instead wish to use this action with a custom GitHub token or custom GitHub app, provide a \`github_token\` in the \`uses\` section of the app in your workflow yml file.`,
);
process.exit(1);
}
}

View File

@@ -4,6 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { readFile } from "fs/promises";
import { join } from "path";
import fetch from "node-fetch";
import { GITHUB_API_URL } from "../github/api/config";
@@ -36,6 +37,7 @@ type GitHubNewCommit = {
const REPO_OWNER = process.env.REPO_OWNER;
const REPO_NAME = process.env.REPO_NAME;
const BRANCH_NAME = process.env.BRANCH_NAME;
const REPO_DIR = process.env.REPO_DIR || process.cwd();
if (!REPO_OWNER || !REPO_NAME || !BRANCH_NAME) {
console.error(
@@ -71,18 +73,9 @@ server.tool(
throw new Error("GITHUB_TOKEN environment variable is required");
}
// Convert absolute paths to relative if they match CWD
const cwd = process.cwd();
const processedFiles = files.map((filePath) => {
if (filePath.startsWith("/")) {
if (filePath.startsWith(cwd)) {
// Strip CWD from absolute path
return filePath.slice(cwd.length + 1);
} else {
throw new Error(
`Path '${filePath}' must be relative to repository root or within current working directory`,
);
}
return filePath.slice(1);
}
return filePath;
});
@@ -126,7 +119,11 @@ server.tool(
// 3. Create tree entries for all files
const treeEntries = await Promise.all(
processedFiles.map(async (filePath) => {
const content = await readFile(filePath, "utf-8");
const fullPath = filePath.startsWith("/")
? filePath
: join(REPO_DIR, filePath);
const content = await readFile(fullPath, "utf-8");
return {
path: filePath,
mode: "100644",
@@ -232,13 +229,16 @@ server.tool(
],
};
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
text: `Error: ${errorMessage}`,
},
],
error: errorMessage,
isError: true,
};
}
@@ -423,13 +423,16 @@ server.tool(
],
};
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
text: `Error: ${errorMessage}`,
},
],
error: errorMessage,
isError: true,
};
}

View File

@@ -34,6 +34,7 @@ export async function prepareMcpConfig(
REPO_OWNER: owner,
REPO_NAME: repo,
BRANCH_NAME: branch,
REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(),
},
},
},