From 55e94369f7a8c06187b79e7276e6ff4129fb6df2 Mon Sep 17 00:00:00 2001 From: km-anthropic Date: Tue, 12 Aug 2025 11:56:00 -0700 Subject: [PATCH] Add GitHub MCP server and context prefix to agent mode - Include main GitHub MCP server (Docker-based) by default - Fetch and prefix GitHub context to prompts when in PR/issue context - Users no longer need to manually configure GitHub tools --- src/modes/agent/index.ts | 117 +++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 10 deletions(-) diff --git a/src/modes/agent/index.ts b/src/modes/agent/index.ts index e9d6ce4..b51d6fb 100644 --- a/src/modes/agent/index.ts +++ b/src/modes/agent/index.ts @@ -2,6 +2,16 @@ import * as core from "@actions/core"; import { mkdir, writeFile } from "fs/promises"; import type { Mode, ModeOptions, ModeResult } from "../types"; import type { PreparedContext } from "../../create-prompt/types"; +import { GITHUB_API_URL, GITHUB_SERVER_URL } from "../../github/api/config"; +import { fetchGitHubData } from "../../github/data/fetcher"; +import { + formatContext, + formatBody, + formatComments, + formatReviewComments, + formatChangedFilesWithSHA +} from "../../github/data/formatter"; +import { isEntityContext } from "../../github/context"; /** * Agent mode implementation. @@ -39,19 +49,62 @@ export const agentMode: Mode = { return false; }, - async prepare({ context, githubToken }: ModeOptions): Promise { + async prepare({ context, githubToken, octokit }: ModeOptions): Promise { // Agent mode handles automation events and any event with explicit prompts - // TODO: handle by createPrompt (similar to tag and review modes) // Create prompt directory await mkdir(`${process.env.RUNNER_TEMP}/claude-prompts`, { recursive: true, }); - // Write the prompt file - the base action requires a prompt_file parameter. - // Use the unified prompt field from v1.0. - const promptContent = - context.inputs.prompt || + + // Fetch GitHub context data if we're in an entity context (PR/issue) + let githubContextPrefix = ''; + if (isEntityContext(context)) { + try { + const githubData = await fetchGitHubData({ + octokits: octokit, + repository: `${context.repository.owner}/${context.repository.repo}`, + prNumber: context.entityNumber.toString(), + isPR: context.isPR, + triggerUsername: context.actor, + }); + + // Format the GitHub data into a readable context + const formattedContext = formatContext(githubData.contextData, context.isPR); + const formattedBody = githubData.contextData?.body + ? formatBody(githubData.contextData.body, githubData.imageUrlMap) + : "No description provided"; + const formattedComments = formatComments(githubData.comments, githubData.imageUrlMap); + + // Build the context prefix + githubContextPrefix = `## GitHub Context + +${formattedContext} + +### Description +${formattedBody}`; + + if (formattedComments && formattedComments.trim()) { + githubContextPrefix += `\n\n### Comments\n${formattedComments}`; + } + + if (context.isPR && githubData.changedFilesWithSHA) { + const formattedFiles = formatChangedFilesWithSHA(githubData.changedFilesWithSHA); + githubContextPrefix += `\n\n### Changed Files\n${formattedFiles}`; + } + + githubContextPrefix += '\n\n## Your Task\n\n'; + } catch (error) { + console.warn('Failed to fetch GitHub context:', error); + // Continue without GitHub context if fetching fails + } + } + + // Write the prompt file with GitHub context prefix + const userPrompt = context.inputs.prompt || `Repository: ${context.repository.owner}/${context.repository.repo}`; + const promptContent = githubContextPrefix + userPrompt; + await writeFile( `${process.env.RUNNER_TEMP}/claude-prompts/claude-prompt.txt`, promptContent, @@ -60,12 +113,12 @@ export const agentMode: Mode = { // Agent mode: User has full control via claudeArgs // No default tools are enforced - Claude Code's defaults will apply - // Always include the GitHub comment server in agent mode - // This ensures GitHub tools (PR reviews, comments, etc.) work out of the box - // without requiring users to manually configure the MCP server + // Include both GitHub comment server and main GitHub MCP server by default + // This ensures comprehensive GitHub tools work out of the box const mcpConfig: any = { mcpServers: { - "github-comment-server": { + // GitHub comment server for updating Claude comments + github_comment: { command: "bun", args: [ "run", @@ -75,13 +128,57 @@ export const agentMode: Mode = { GITHUB_TOKEN: githubToken || "", REPO_OWNER: context.repository.owner, REPO_NAME: context.repository.repo, + CLAUDE_COMMENT_ID: process.env.CLAUDE_COMMENT_ID || "", + PR_NUMBER: (context as any).entityNumber?.toString() || process.env.GITHUB_EVENT_PULL_REQUEST_NUMBER || "", + ISSUE_NUMBER: (context as any).entityNumber?.toString() || "", GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || "", GITHUB_API_URL: process.env.GITHUB_API_URL || "https://api.github.com", }, }, + // Main GitHub MCP server for comprehensive GitHub operations + 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, + }, + }, }, }; + + // Include inline comment server for PR contexts + if (context.eventName === "pull_request" || context.eventName === "pull_request_review") { + // Get PR number from the context payload + const prNumber = (context as any).payload?.pull_request?.number || + (context as any).entityNumber || + ""; + + mcpConfig.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: context.repository.owner, + REPO_NAME: context.repository.repo, + PR_NUMBER: prNumber.toString(), + GITHUB_API_URL: process.env.GITHUB_API_URL || "https://api.github.com", + }, + }; + } // Add user-provided additional MCP config if any const additionalMcpConfig = process.env.MCP_CONFIG || "";