mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
* enable track_progress for comments * refactor: pass mode explicitly to prepareMcpConfig Update prepareMcpConfig to receive the mode parameter from its callers instead of detecting agent mode by checking context.inputs.prompt. This makes mode determination explicit and controlled by the caller. Also update all test cases to include the required mode parameter and fix agent mode test expectations to match new behavior where MCP config is only included when tools are explicitly allowed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix test --------- Co-authored-by: Claude <noreply@anthropic.com>
223 lines
6.4 KiB
TypeScript
223 lines
6.4 KiB
TypeScript
import * as core from "@actions/core";
|
|
import { GITHUB_API_URL, GITHUB_SERVER_URL } from "../github/api/config";
|
|
import type { GitHubContext } from "../github/context";
|
|
import { isEntityContext } from "../github/context";
|
|
import { Octokit } from "@octokit/rest";
|
|
|
|
type PrepareConfigParams = {
|
|
githubToken: string;
|
|
owner: string;
|
|
repo: string;
|
|
branch: string;
|
|
baseBranch: string;
|
|
claudeCommentId?: string;
|
|
allowedTools: string[];
|
|
context: GitHubContext;
|
|
mode: "tag" | "agent";
|
|
};
|
|
|
|
async function checkActionsReadPermission(
|
|
token: string,
|
|
owner: string,
|
|
repo: string,
|
|
): Promise<boolean> {
|
|
try {
|
|
const client = new Octokit({ auth: token, baseUrl: GITHUB_API_URL });
|
|
|
|
// Try to list workflow runs - this requires actions:read
|
|
// We use per_page=1 to minimize the response size
|
|
await client.actions.listWorkflowRunsForRepo({
|
|
owner,
|
|
repo,
|
|
per_page: 1,
|
|
});
|
|
|
|
return true;
|
|
} catch (error: any) {
|
|
// Check if it's a permission error
|
|
if (
|
|
error.status === 403 &&
|
|
error.message?.includes("Resource not accessible")
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// For other errors (network issues, etc), log but don't fail
|
|
core.debug(`Failed to check actions permission: ${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function prepareMcpConfig(
|
|
params: PrepareConfigParams,
|
|
): Promise<string> {
|
|
const {
|
|
githubToken,
|
|
owner,
|
|
repo,
|
|
branch,
|
|
baseBranch,
|
|
claudeCommentId,
|
|
allowedTools,
|
|
context,
|
|
mode,
|
|
} = params;
|
|
try {
|
|
const allowedToolsList = allowedTools || [];
|
|
|
|
const isAgentMode = mode === "agent";
|
|
|
|
const hasGitHubMcpTools = allowedToolsList.some((tool) =>
|
|
tool.startsWith("mcp__github__"),
|
|
);
|
|
|
|
const hasInlineCommentTools = allowedToolsList.some((tool) =>
|
|
tool.startsWith("mcp__github_inline_comment__"),
|
|
);
|
|
|
|
const hasGitHubCITools = allowedToolsList.some((tool) =>
|
|
tool.startsWith("mcp__github_ci__"),
|
|
);
|
|
|
|
const baseMcpConfig: { mcpServers: Record<string, unknown> } = {
|
|
mcpServers: {},
|
|
};
|
|
|
|
// Include comment server:
|
|
// - Always in tag mode (for updating Claude comments)
|
|
// - Only with explicit tools in agent mode
|
|
const shouldIncludeCommentServer = !isAgentMode;
|
|
|
|
if (shouldIncludeCommentServer) {
|
|
baseMcpConfig.mcpServers.github_comment = {
|
|
command: "bun",
|
|
args: [
|
|
"run",
|
|
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-comment-server.ts`,
|
|
],
|
|
env: {
|
|
GITHUB_TOKEN: githubToken,
|
|
REPO_OWNER: owner,
|
|
REPO_NAME: repo,
|
|
...(claudeCommentId && { CLAUDE_COMMENT_ID: claudeCommentId }),
|
|
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "",
|
|
GITHUB_API_URL: GITHUB_API_URL,
|
|
},
|
|
};
|
|
}
|
|
|
|
// Include file ops server when commit signing is enabled
|
|
if (context.inputs.useCommitSigning) {
|
|
baseMcpConfig.mcpServers.github_file_ops = {
|
|
command: "bun",
|
|
args: [
|
|
"run",
|
|
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-file-ops-server.ts`,
|
|
],
|
|
env: {
|
|
GITHUB_TOKEN: githubToken,
|
|
REPO_OWNER: owner,
|
|
REPO_NAME: repo,
|
|
BRANCH_NAME: branch,
|
|
BASE_BRANCH: baseBranch,
|
|
REPO_DIR: process.env.GITHUB_WORKSPACE || process.cwd(),
|
|
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "",
|
|
IS_PR: process.env.IS_PR || "false",
|
|
GITHUB_API_URL: GITHUB_API_URL,
|
|
},
|
|
};
|
|
}
|
|
|
|
// Include inline comment server for PRs when requested via allowed tools
|
|
if (
|
|
isEntityContext(context) &&
|
|
context.isPR &&
|
|
(hasGitHubMcpTools || hasInlineCommentTools)
|
|
) {
|
|
baseMcpConfig.mcpServers.github_inline_comment = {
|
|
command: "bun",
|
|
args: [
|
|
"run",
|
|
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-inline-comment-server.ts`,
|
|
],
|
|
env: {
|
|
GITHUB_TOKEN: githubToken,
|
|
REPO_OWNER: owner,
|
|
REPO_NAME: repo,
|
|
PR_NUMBER: context.entityNumber?.toString() || "",
|
|
GITHUB_API_URL: GITHUB_API_URL,
|
|
},
|
|
};
|
|
}
|
|
|
|
// CI server is included when:
|
|
// - In tag mode: when we have a workflow token and context is a PR
|
|
// - In agent mode: same conditions PLUS explicit CI tools in allowedTools
|
|
const hasWorkflowToken = !!process.env.DEFAULT_WORKFLOW_TOKEN;
|
|
const shouldIncludeCIServer =
|
|
(!isAgentMode || hasGitHubCITools) &&
|
|
isEntityContext(context) &&
|
|
context.isPR &&
|
|
hasWorkflowToken;
|
|
|
|
if (shouldIncludeCIServer) {
|
|
// Verify the token actually has actions:read permission
|
|
const actuallyHasPermission = await checkActionsReadPermission(
|
|
process.env.DEFAULT_WORKFLOW_TOKEN || "",
|
|
owner,
|
|
repo,
|
|
);
|
|
|
|
if (!actuallyHasPermission) {
|
|
core.warning(
|
|
"The github_ci MCP server requires 'actions: read' permission. " +
|
|
"Please ensure your GitHub token has this permission. " +
|
|
"See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token",
|
|
);
|
|
}
|
|
baseMcpConfig.mcpServers.github_ci = {
|
|
command: "bun",
|
|
args: [
|
|
"run",
|
|
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-actions-server.ts`,
|
|
],
|
|
env: {
|
|
// Use workflow github token, not app token
|
|
GITHUB_TOKEN: process.env.DEFAULT_WORKFLOW_TOKEN,
|
|
REPO_OWNER: owner,
|
|
REPO_NAME: repo,
|
|
PR_NUMBER: context.entityNumber?.toString() || "",
|
|
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
|
|
},
|
|
};
|
|
}
|
|
|
|
if (hasGitHubMcpTools) {
|
|
baseMcpConfig.mcpServers.github = {
|
|
command: "docker",
|
|
args: [
|
|
"run",
|
|
"-i",
|
|
"--rm",
|
|
"-e",
|
|
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
|
"-e",
|
|
"GITHUB_HOST",
|
|
"ghcr.io/github/github-mcp-server:sha-efef8ae", // https://github.com/github/github-mcp-server/releases/tag/v0.9.0
|
|
],
|
|
env: {
|
|
GITHUB_PERSONAL_ACCESS_TOKEN: githubToken,
|
|
GITHUB_HOST: GITHUB_SERVER_URL,
|
|
},
|
|
};
|
|
}
|
|
|
|
// Return only our GitHub servers config
|
|
// User's config will be passed as separate --mcp-config flags
|
|
return JSON.stringify(baseMcpConfig, null, 2);
|
|
} catch (error) {
|
|
core.setFailed(`Install MCP server failed with error: ${error}`);
|
|
process.exit(1);
|
|
}
|
|
}
|