Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions src/mcp/install-mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,27 @@ export async function prepareMcpConfig(
if (!actuallyHasPermission) {
core.warning(
"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. " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The guidance here covers adding actions: read to the workflow's permissions: block, but users of this action also need to set additional_permissions: 'actions: read' in the action's with: inputs (per docs/usage.md). Consider linking to the project's own configuration docs instead of (or in addition to) the generic GitHub token docs, so users can find both steps in one place.

"See: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token",
);
} else {
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",
},
};
}
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) {
Expand Down
33 changes: 33 additions & 0 deletions test/install-mcp-server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe("prepareMcpConfig", () => {
let consoleWarningSpy: any;
let setFailedSpy: any;
let processExitSpy: any;
let fetchSpy: any;

// Create a mock context for tests
const mockContext: ParsedGitHubContext = {
Expand Down Expand Up @@ -66,6 +67,10 @@ describe("prepareMcpConfig", () => {
processExitSpy = spyOn(process, "exit").mockImplementation(() => {
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
if (!process.env.GITHUB_ACTION_PATH) {
Expand All @@ -78,6 +83,7 @@ describe("prepareMcpConfig", () => {
consoleWarningSpy.mockRestore();
setFailedSpy.mockRestore();
processExitSpy.mockRestore();
fetchSpy.mockRestore();
});

test("should return comment server when commit signing is disabled", async () => {
Expand Down Expand Up @@ -263,6 +269,33 @@ describe("prepareMcpConfig", () => {
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;
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Since this PR changed the warning text, it would be worth asserting that core.warning was called with the expected message (e.g., matching "Skipping CI server installation"). This would guard against regressions in the warning behavior and confirm the graceful-degradation UX, not just the config output.

test("should not include github_ci server when DEFAULT_WORKFLOW_TOKEN is missing", async () => {
delete process.env.DEFAULT_WORKFLOW_TOKEN;

Expand Down
Loading