mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 06:54:13 +08:00
feat: add MCP servers to agent mode
- Update agent mode to use prepareMcpConfig instead of empty config - Update prepareMcpConfig to accept both ParsedGitHubContext and AutomationContext - Add type guards for context-specific properties (isPR, entityNumber) - This enables bot actors to use full MCP capabilities via workflow_dispatch
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { GITHUB_API_URL, GITHUB_SERVER_URL } from "../github/api/config";
|
import { GITHUB_API_URL, GITHUB_SERVER_URL } from "../github/api/config";
|
||||||
import type { ParsedGitHubContext } from "../github/context";
|
import type { GitHubContext } from "../github/context";
|
||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
|
|
||||||
type PrepareConfigParams = {
|
type PrepareConfigParams = {
|
||||||
@@ -12,7 +12,7 @@ type PrepareConfigParams = {
|
|||||||
additionalMcpConfig?: string;
|
additionalMcpConfig?: string;
|
||||||
claudeCommentId?: string;
|
claudeCommentId?: string;
|
||||||
allowedTools: string[];
|
allowedTools: string[];
|
||||||
context: ParsedGitHubContext;
|
context: GitHubContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function checkActionsReadPermission(
|
async function checkActionsReadPermission(
|
||||||
@@ -115,7 +115,7 @@ export async function prepareMcpConfig(
|
|||||||
const hasActionsReadPermission =
|
const hasActionsReadPermission =
|
||||||
context.inputs.additionalPermissions.get("actions") === "read";
|
context.inputs.additionalPermissions.get("actions") === "read";
|
||||||
|
|
||||||
if (context.isPR && hasActionsReadPermission) {
|
if ('isPR' in context && context.isPR && hasActionsReadPermission) {
|
||||||
// Verify the token actually has actions:read permission
|
// Verify the token actually has actions:read permission
|
||||||
const actuallyHasPermission = await checkActionsReadPermission(
|
const actuallyHasPermission = await checkActionsReadPermission(
|
||||||
process.env.ACTIONS_TOKEN || "",
|
process.env.ACTIONS_TOKEN || "",
|
||||||
@@ -141,7 +141,7 @@ export async function prepareMcpConfig(
|
|||||||
GITHUB_TOKEN: process.env.ACTIONS_TOKEN,
|
GITHUB_TOKEN: process.env.ACTIONS_TOKEN,
|
||||||
REPO_OWNER: owner,
|
REPO_OWNER: owner,
|
||||||
REPO_NAME: repo,
|
REPO_NAME: repo,
|
||||||
PR_NUMBER: context.entityNumber?.toString() || "",
|
PR_NUMBER: 'entityNumber' in context ? context.entityNumber?.toString() || "" : "",
|
||||||
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
|
RUNNER_TEMP: process.env.RUNNER_TEMP || "/tmp",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { mkdir, writeFile } from "fs/promises";
|
|
||||||
import type { Mode, ModeOptions, ModeResult } from "../types";
|
import type { Mode, ModeOptions, ModeResult } from "../types";
|
||||||
import { isAutomationContext } from "../../github/context";
|
import { isAutomationContext } from "../../github/context";
|
||||||
|
import type { PreparedContext } from "../../create-prompt/types";
|
||||||
|
import { prepareMcpConfig } from "../../mcp/install-mcp-server";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent mode implementation.
|
* Agent mode implementation.
|
||||||
@@ -39,27 +40,10 @@ export const agentMode: Mode = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
async prepare({ context }: ModeOptions): Promise<ModeResult> {
|
async prepare({ context, githubToken }: ModeOptions): Promise<ModeResult> {
|
||||||
// Agent mode handles automation events (workflow_dispatch, schedule) only
|
// Agent mode handles automation events (workflow_dispatch, schedule) only
|
||||||
|
|
||||||
// Create prompt directory
|
// Agent mode doesn't need to create prompt files here - handled by createPrompt
|
||||||
await mkdir(`${process.env.RUNNER_TEMP}/claude-prompts`, {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write the prompt file - the base action requires a prompt_file parameter,
|
|
||||||
// so we must create this file even though agent mode typically uses
|
|
||||||
// override_prompt or direct_prompt. If neither is provided, we write
|
|
||||||
// a minimal prompt with just the repository information.
|
|
||||||
const promptContent =
|
|
||||||
context.inputs.overridePrompt ||
|
|
||||||
context.inputs.directPrompt ||
|
|
||||||
`Repository: ${context.repository.owner}/${context.repository.repo}`;
|
|
||||||
|
|
||||||
await writeFile(
|
|
||||||
`${process.env.RUNNER_TEMP}/claude-prompts/claude-prompt.txt`,
|
|
||||||
promptContent,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Export tool environment variables for agent mode
|
// Export tool environment variables for agent mode
|
||||||
const baseTools = [
|
const baseTools = [
|
||||||
@@ -80,29 +64,25 @@ export const agentMode: Mode = {
|
|||||||
...context.inputs.disallowedTools,
|
...context.inputs.disallowedTools,
|
||||||
];
|
];
|
||||||
|
|
||||||
core.exportVariable("ALLOWED_TOOLS", allowedTools.join(","));
|
// Export as INPUT_ prefixed variables for the base action
|
||||||
core.exportVariable("DISALLOWED_TOOLS", disallowedTools.join(","));
|
core.exportVariable("INPUT_ALLOWED_TOOLS", allowedTools.join(","));
|
||||||
|
core.exportVariable("INPUT_DISALLOWED_TOOLS", disallowedTools.join(","));
|
||||||
|
|
||||||
// Agent mode uses a minimal MCP configuration
|
// Get MCP configuration using the same setup as other modes
|
||||||
// We don't need comment servers or PR-specific tools for automation
|
|
||||||
const mcpConfig: any = {
|
|
||||||
mcpServers: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add user-provided additional MCP config if any
|
|
||||||
const additionalMcpConfig = process.env.MCP_CONFIG || "";
|
const additionalMcpConfig = process.env.MCP_CONFIG || "";
|
||||||
if (additionalMcpConfig.trim()) {
|
const mcpConfig = await prepareMcpConfig({
|
||||||
try {
|
githubToken,
|
||||||
const additional = JSON.parse(additionalMcpConfig);
|
owner: context.repository.owner,
|
||||||
if (additional && typeof additional === "object") {
|
repo: context.repository.repo,
|
||||||
Object.assign(mcpConfig, additional);
|
branch: "", // Agent mode doesn't use branches
|
||||||
}
|
baseBranch: "",
|
||||||
} catch (error) {
|
additionalMcpConfig,
|
||||||
core.warning(`Failed to parse additional MCP config: ${error}`);
|
claudeCommentId: undefined, // Agent mode doesn't track comments
|
||||||
}
|
allowedTools: [...baseTools, ...context.inputs.allowedTools],
|
||||||
}
|
context,
|
||||||
|
});
|
||||||
|
|
||||||
core.setOutput("mcp_config", JSON.stringify(mcpConfig));
|
core.setOutput("mcp_config", mcpConfig);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commentId: undefined,
|
commentId: undefined,
|
||||||
@@ -111,7 +91,21 @@ export const agentMode: Mode = {
|
|||||||
currentBranch: "",
|
currentBranch: "",
|
||||||
claudeBranch: undefined,
|
claudeBranch: undefined,
|
||||||
},
|
},
|
||||||
mcpConfig: JSON.stringify(mcpConfig),
|
mcpConfig: mcpConfig,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
generatePrompt(context: PreparedContext): string {
|
||||||
|
// Agent mode uses override or direct prompt, no GitHub data needed
|
||||||
|
if (context.overridePrompt) {
|
||||||
|
return context.overridePrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.directPrompt) {
|
||||||
|
return context.directPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal fallback - repository is a string in PreparedContext
|
||||||
|
return `Repository: ${context.repository}`;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user