diff --git a/src/modes/tag/index.ts b/src/modes/tag/index.ts index bfbeaea54..8e39c8cda 100644 --- a/src/modes/tag/index.ts +++ b/src/modes/tag/index.ts @@ -132,6 +132,12 @@ export async function prepareTagMode({ ...userAllowedMCPTools, ]; + // Enable inline PR comments for PR contexts so Claude can post + // review feedback directly on diff lines alongside the tracking comment + if (context.isPR) { + tagModeTools.push("mcp__github_inline_comment__create_inline_comment"); + } + // Add git commands when using git CLI (no API commit signing, or SSH signing) // SSH signing still uses git CLI, just with signing enabled if (!useApiCommitSigning) { @@ -149,6 +155,10 @@ export async function prepareTagMode({ ); } + // Dedupe once so both the MCP config and the --allowedTools CLI flag + // see the same set (user-supplied CLAUDE_ARGS may include tools we also push) + const dedupedTools = Array.from(new Set(tagModeTools)); + // Get our GitHub MCP servers configuration const ourMcpConfig = await prepareMcpConfig({ githubToken, @@ -157,7 +167,7 @@ export async function prepareTagMode({ branch: branchInfo.claudeBranch || branchInfo.currentBranch, baseBranch: branchInfo.baseBranch, claudeCommentId: commentId.toString(), - allowedTools: Array.from(new Set(tagModeTools)), + allowedTools: dedupedTools, mode: "tag", context, }); @@ -172,7 +182,7 @@ export async function prepareTagMode({ // Add required tools for tag mode. // acceptEdits: file edits auto-allowed inside cwd ($GITHUB_WORKSPACE), denied outside. // Headless SDK has no prompt handler, so anything that falls through to "ask" is denied. - claudeArgs += ` --permission-mode acceptEdits --allowedTools "${tagModeTools.join(",")}"`; + claudeArgs += ` --permission-mode acceptEdits --allowedTools "${dedupedTools.join(",")}"`; // Append user's claude_args (which may have more --mcp-config flags) if (userClaudeArgs) { diff --git a/test/install-mcp-server.test.ts b/test/install-mcp-server.test.ts index 87c751365..25319569d 100644 --- a/test/install-mcp-server.test.ts +++ b/test/install-mcp-server.test.ts @@ -314,4 +314,47 @@ describe("prepareMcpConfig", () => { const parsed = JSON.parse(result); expect(parsed.mcpServers.github_ci).not.toBeDefined(); }); + + test("should not include inline comment server for non-PR contexts", async () => { + const result = await prepareMcpConfig({ + githubToken: "test-token", + owner: "test-owner", + repo: "test-repo", + branch: "test-branch", + baseBranch: "main", + allowedTools: ["mcp__github_inline_comment__create_inline_comment"], + mode: "tag", + context: mockContext, // isPR: false + }); + + const parsed = JSON.parse(result); + expect(parsed.mcpServers.github_inline_comment).not.toBeDefined(); + }); + + test("should include both comment and inline comment servers for PR contexts", async () => { + const mockPRContextWithSticky: ParsedGitHubContext = { + ...mockPRContext, + inputs: { + ...mockPRContext.inputs, + useStickyComment: true, + }, + }; + + const result = await prepareMcpConfig({ + githubToken: "test-token", + owner: "test-owner", + repo: "test-repo", + branch: "test-branch", + baseBranch: "main", + allowedTools: ["mcp__github_inline_comment__create_inline_comment"], + mode: "tag", + context: mockPRContextWithSticky, + }); + + const parsed = JSON.parse(result); + // Both comment server (for sticky tracking comment) and inline comment server should coexist + expect(parsed.mcpServers.github_comment).toBeDefined(); + expect(parsed.mcpServers.github_inline_comment).toBeDefined(); + expect(parsed.mcpServers.github_inline_comment.env.PR_NUMBER).toBe("456"); + }); });