Skip to content

Commit a3fea12

Browse files
committed
fix: skip CI MCP server installation when actions:read permission is missing
1 parent 1bb0e74 commit a3fea12

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

src/mcp/install-mcp-server.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -177,25 +177,27 @@ export async function prepareMcpConfig(
177177
if (!actuallyHasPermission) {
178178
core.warning(
179179
"The github_ci MCP server requires 'actions: read' permission. " +
180-
"Please ensure your GitHub token has this permission. " +
180+
"Skipping CI server installation. " +
181+
"To enable CI status checks, add 'actions: read' to your workflow permissions. " +
181182
"See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token",
182183
);
184+
} else {
185+
baseMcpConfig.mcpServers.github_ci = {
186+
command: "bun",
187+
args: [
188+
"run",
189+
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-actions-server.ts`,
190+
],
191+
env: {
192+
// Use workflow github token, not app token
193+
GITHUB_TOKEN: process.env.DEFAULT_WORKFLOW_TOKEN,
194+
REPO_OWNER: owner,
195+
REPO_NAME: repo,
196+
PR_NUMBER: context.entityNumber?.toString() || "",
197+
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
198+
},
199+
};
183200
}
184-
baseMcpConfig.mcpServers.github_ci = {
185-
command: "bun",
186-
args: [
187-
"run",
188-
`${process.env.GITHUB_ACTION_PATH}/src/mcp/github-actions-server.ts`,
189-
],
190-
env: {
191-
// Use workflow github token, not app token
192-
GITHUB_TOKEN: process.env.DEFAULT_WORKFLOW_TOKEN,
193-
REPO_OWNER: owner,
194-
REPO_NAME: repo,
195-
PR_NUMBER: context.entityNumber?.toString() || "",
196-
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
197-
},
198-
};
199201
}
200202

201203
if (hasGitHubMcpTools) {

test/install-mcp-server.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe("prepareMcpConfig", () => {
99
let consoleWarningSpy: any;
1010
let setFailedSpy: any;
1111
let processExitSpy: any;
12+
let fetchSpy: any;
1213

1314
// Create a mock context for tests
1415
const mockContext: ParsedGitHubContext = {
@@ -66,6 +67,10 @@ describe("prepareMcpConfig", () => {
6667
processExitSpy = spyOn(process, "exit").mockImplementation(() => {
6768
throw new Error("Process exit");
6869
});
70+
// Mock fetch so checkActionsReadPermission succeeds (returns 200 for actions API)
71+
fetchSpy = spyOn(global, "fetch").mockResolvedValue(
72+
new Response(JSON.stringify({ workflow_runs: [] }), { status: 200 }),
73+
);
6974

7075
// Set up required environment variables
7176
if (!process.env.GITHUB_ACTION_PATH) {
@@ -78,6 +83,7 @@ describe("prepareMcpConfig", () => {
7883
consoleWarningSpy.mockRestore();
7984
setFailedSpy.mockRestore();
8085
processExitSpy.mockRestore();
86+
fetchSpy.mockRestore();
8187
});
8288

8389
test("should return comment server when commit signing is disabled", async () => {
@@ -263,6 +269,33 @@ describe("prepareMcpConfig", () => {
263269
expect(parsed.mcpServers.github_ci).not.toBeDefined();
264270
});
265271

272+
test("should not include github_ci server when actions:read permission is missing", async () => {
273+
process.env.DEFAULT_WORKFLOW_TOKEN = "workflow-token";
274+
// Simulate 403 from actions API
275+
fetchSpy.mockResolvedValue(
276+
new Response(
277+
JSON.stringify({ message: "Resource not accessible by integration" }),
278+
{ status: 403 },
279+
),
280+
);
281+
282+
const result = await prepareMcpConfig({
283+
githubToken: "test-token",
284+
owner: "test-owner",
285+
repo: "test-repo",
286+
branch: "test-branch",
287+
baseBranch: "main",
288+
allowedTools: [],
289+
mode: "tag",
290+
context: mockPRContext,
291+
});
292+
293+
const parsed = JSON.parse(result);
294+
expect(parsed.mcpServers.github_ci).not.toBeDefined();
295+
296+
delete process.env.DEFAULT_WORKFLOW_TOKEN;
297+
});
298+
266299
test("should not include github_ci server when DEFAULT_WORKFLOW_TOKEN is missing", async () => {
267300
delete process.env.DEFAULT_WORKFLOW_TOKEN;
268301

0 commit comments

Comments
 (0)