fix: skip CI MCP server installation when actions:read permission is missing (#933)

This commit is contained in:
Octavian Guzu 2026-02-11 19:02:49 +00:00 committed by GitHub
parent 1bb0e7464b
commit 8c383c5de3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 16 deletions

View File

@ -177,10 +177,11 @@ export async function prepareMcpConfig(
if (!actuallyHasPermission) { if (!actuallyHasPermission) {
core.warning( core.warning(
"The github_ci MCP server requires 'actions: read' permission. " + "The github_ci MCP server requires 'actions: read' permission. " +
"Please ensure your GitHub token has this permission. " + "Skipping CI server installation. " +
"To enable CI status checks, add 'actions: read' to your workflow permissions. " +
"See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token", "See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token",
); );
} } else {
baseMcpConfig.mcpServers.github_ci = { baseMcpConfig.mcpServers.github_ci = {
command: "bun", command: "bun",
args: [ args: [
@ -197,6 +198,7 @@ export async function prepareMcpConfig(
}, },
}; };
} }
}
if (hasGitHubMcpTools) { if (hasGitHubMcpTools) {
baseMcpConfig.mcpServers.github = { baseMcpConfig.mcpServers.github = {

View File

@ -9,6 +9,7 @@ describe("prepareMcpConfig", () => {
let consoleWarningSpy: any; let consoleWarningSpy: any;
let setFailedSpy: any; let setFailedSpy: any;
let processExitSpy: any; let processExitSpy: any;
let fetchSpy: any;
// Create a mock context for tests // Create a mock context for tests
const mockContext: ParsedGitHubContext = { const mockContext: ParsedGitHubContext = {
@ -66,6 +67,10 @@ describe("prepareMcpConfig", () => {
processExitSpy = spyOn(process, "exit").mockImplementation(() => { processExitSpy = spyOn(process, "exit").mockImplementation(() => {
throw new Error("Process exit"); throw new Error("Process exit");
}); });
// Mock fetch so checkActionsReadPermission succeeds (returns 200 for actions API)
fetchSpy = spyOn(global, "fetch").mockResolvedValue(
new Response(JSON.stringify({ workflow_runs: [] }), { status: 200 }),
);
// Set up required environment variables // Set up required environment variables
if (!process.env.GITHUB_ACTION_PATH) { if (!process.env.GITHUB_ACTION_PATH) {
@ -78,6 +83,7 @@ describe("prepareMcpConfig", () => {
consoleWarningSpy.mockRestore(); consoleWarningSpy.mockRestore();
setFailedSpy.mockRestore(); setFailedSpy.mockRestore();
processExitSpy.mockRestore(); processExitSpy.mockRestore();
fetchSpy.mockRestore();
}); });
test("should return comment server when commit signing is disabled", async () => { test("should return comment server when commit signing is disabled", async () => {
@ -263,6 +269,33 @@ describe("prepareMcpConfig", () => {
expect(parsed.mcpServers.github_ci).not.toBeDefined(); expect(parsed.mcpServers.github_ci).not.toBeDefined();
}); });
test("should not include github_ci server when actions:read permission is missing", async () => {
process.env.DEFAULT_WORKFLOW_TOKEN = "workflow-token";
// Simulate 403 from actions API
fetchSpy.mockResolvedValue(
new Response(
JSON.stringify({ message: "Resource not accessible by integration" }),
{ status: 403 },
),
);
const result = await prepareMcpConfig({
githubToken: "test-token",
owner: "test-owner",
repo: "test-repo",
branch: "test-branch",
baseBranch: "main",
allowedTools: [],
mode: "tag",
context: mockPRContext,
});
const parsed = JSON.parse(result);
expect(parsed.mcpServers.github_ci).not.toBeDefined();
delete process.env.DEFAULT_WORKFLOW_TOKEN;
});
test("should not include github_ci server when DEFAULT_WORKFLOW_TOKEN is missing", async () => { test("should not include github_ci server when DEFAULT_WORKFLOW_TOKEN is missing", async () => {
delete process.env.DEFAULT_WORKFLOW_TOKEN; delete process.env.DEFAULT_WORKFLOW_TOKEN;