Attempt to make this work

This commit is contained in:
Mark Wylde
2025-05-30 23:55:03 +01:00
parent bbf8371776
commit 8de76049e1
4 changed files with 138 additions and 38 deletions

View File

@@ -2,6 +2,8 @@
import Anthropic from "@anthropic-ai/sdk"; import Anthropic from "@anthropic-ai/sdk";
import * as fs from "fs"; import * as fs from "fs";
import { spawn } from "child_process";
import { promisify } from "util";
export interface ClaudeExecutorConfig { export interface ClaudeExecutorConfig {
apiKey?: string; apiKey?: string;
@@ -23,43 +25,43 @@ export interface ClaudeExecutorResult {
error?: string; error?: string;
} }
interface MCPServer {
command: string;
args: string[];
env?: Record<string, string>;
}
interface MCPConfig {
mcpServers: Record<string, MCPServer>;
}
export class ClaudeExecutor { export class ClaudeExecutor {
private config: ClaudeExecutorConfig; private config: ClaudeExecutorConfig;
private anthropic?: Anthropic;
constructor(config: ClaudeExecutorConfig) { constructor(config: ClaudeExecutorConfig) {
this.config = config; this.config = config;
this.initializeClient();
}
private initializeClient() {
if (this.config.useBedrock || this.config.useVertex) {
throw new Error(
"Bedrock and Vertex AI not supported in simplified implementation",
);
}
if (!this.config.apiKey) {
throw new Error("Anthropic API key is required");
}
this.anthropic = new Anthropic({
apiKey: this.config.apiKey,
});
} }
private async readPrompt(): Promise<string> { private async readPrompt(): Promise<string> {
console.log("[CLAUDE-EXECUTOR] Reading prompt...");
if (this.config.prompt) { if (this.config.prompt) {
console.log("[CLAUDE-EXECUTOR] Using direct prompt");
return this.config.prompt; return this.config.prompt;
} }
if (this.config.promptFile) { if (this.config.promptFile) {
console.log(`[CLAUDE-EXECUTOR] Using prompt file: ${this.config.promptFile}`);
if (!fs.existsSync(this.config.promptFile)) { if (!fs.existsSync(this.config.promptFile)) {
console.error(`[CLAUDE-EXECUTOR] Prompt file not found: ${this.config.promptFile}`);
throw new Error(`Prompt file not found: ${this.config.promptFile}`); throw new Error(`Prompt file not found: ${this.config.promptFile}`);
} }
return fs.readFileSync(this.config.promptFile, "utf-8"); const content = fs.readFileSync(this.config.promptFile, "utf-8");
console.log(`[CLAUDE-EXECUTOR] Prompt file size: ${content.length} bytes`);
return content;
} }
console.error("[CLAUDE-EXECUTOR] Neither prompt nor promptFile provided");
throw new Error("Either prompt or promptFile must be provided"); throw new Error("Either prompt or promptFile must be provided");
} }
@@ -95,23 +97,63 @@ export class ClaudeExecutor {
return logFile; return logFile;
} }
private async setupMCPConfig(): Promise<string | null> {
if (!this.config.mcpConfig) {
console.log("[CLAUDE-EXECUTOR] No MCP config provided");
return null;
}
try {
const mcpConfig: MCPConfig = JSON.parse(this.config.mcpConfig);
const configFile = "/tmp/mcp-config.json";
console.log("[CLAUDE-EXECUTOR] Setting up MCP configuration...");
console.log("[CLAUDE-EXECUTOR] MCP servers:", Object.keys(mcpConfig.mcpServers));
fs.writeFileSync(configFile, JSON.stringify(mcpConfig, null, 2));
console.log("[CLAUDE-EXECUTOR] MCP config written to:", configFile);
return configFile;
} catch (error) {
console.error("[CLAUDE-EXECUTOR] Failed to parse MCP config:", error);
return null;
}
}
async execute(): Promise<ClaudeExecutorResult> { async execute(): Promise<ClaudeExecutorResult> {
try { try {
const prompt = await this.readPrompt(); const prompt = await this.readPrompt();
const tools = this.parseTools(); const tools = this.parseTools();
console.log(`[CLAUDE-EXECUTOR] Prompt length: ${prompt.length} characters`);
console.log( console.log(
`Executing Claude with model: ${this.config.model || "claude-3-7-sonnet-20250219"}`, `[CLAUDE-EXECUTOR] Executing Claude with model: ${this.config.model || "claude-3-7-sonnet-20250219"}`,
); );
console.log(`Allowed tools: ${tools.allowed.join(", ") || "none"}`); console.log(`[CLAUDE-EXECUTOR] Allowed tools: ${tools.allowed.join(", ") || "none"}`);
console.log(`Disallowed tools: ${tools.disallowed.join(", ") || "none"}`); console.log(`[CLAUDE-EXECUTOR] Disallowed tools: ${tools.disallowed.join(", ") || "none"}`);
if (!this.anthropic) { // For now, just log that we're using the simple API approach
throw new Error("Anthropic client not initialized"); console.log("[CLAUDE-EXECUTOR] WARNING: Using simple Anthropic API - MCP tools not supported in this implementation");
console.log("[CLAUDE-EXECUTOR] This explains why Claude cannot use mcp__local_git_ops tools");
if (this.config.mcpConfig) {
console.log("[CLAUDE-EXECUTOR] MCP config provided but not used:");
console.log(this.config.mcpConfig.substring(0, 200) + "...");
} }
if (!this.config.apiKey) {
throw new Error("Anthropic API key is required");
}
const anthropic = new Anthropic({
apiKey: this.config.apiKey,
});
console.log("[CLAUDE-EXECUTOR] Starting simple Anthropic API call...");
console.log("[CLAUDE-EXECUTOR] Prompt preview:", prompt.substring(0, 500) + "...");
// Create a simple message request // Create a simple message request
const response = await this.anthropic.messages.create({ const response = await anthropic.messages.create({
model: this.config.model || "claude-3-7-sonnet-20250219", model: this.config.model || "claude-3-7-sonnet-20250219",
max_tokens: 8192, max_tokens: 8192,
messages: [ messages: [
@@ -122,7 +164,12 @@ export class ClaudeExecutor {
], ],
}); });
console.log("Claude response received successfully"); console.log("[CLAUDE-EXECUTOR] Claude response received successfully");
console.log("[CLAUDE-EXECUTOR] Response type:", response.content[0]?.type);
if (response.content[0]?.type === "text") {
console.log("[CLAUDE-EXECUTOR] Response preview:", response.content[0].text.substring(0, 500) + "...");
}
const executionFile = this.createExecutionLog(response); const executionFile = this.createExecutionLog(response);
@@ -131,7 +178,7 @@ export class ClaudeExecutor {
executionFile, executionFile,
}; };
} catch (error) { } catch (error) {
console.error("Claude execution failed:", error); console.error("[CLAUDE-EXECUTOR] Claude execution failed:", error);
const executionFile = this.createExecutionLog(null, String(error)); const executionFile = this.createExecutionLog(null, String(error));

View File

@@ -5,6 +5,17 @@ import { runClaude, type ClaudeExecutorConfig } from "../claude/executor";
async function main() { async function main() {
try { try {
console.log("[EXECUTE-CLAUDE] Starting execute-claude.ts entry point...");
console.log(`[EXECUTE-CLAUDE] ANTHROPIC_API_KEY: ${process.env.ANTHROPIC_API_KEY ? '***' : 'undefined'}`);
console.log(`[EXECUTE-CLAUDE] MODEL: ${process.env.MODEL || 'undefined'}`);
console.log(`[EXECUTE-CLAUDE] ANTHROPIC_MODEL: ${process.env.ANTHROPIC_MODEL || 'undefined'}`);
console.log(`[EXECUTE-CLAUDE] PROMPT_FILE: ${process.env.PROMPT_FILE || 'undefined'}`);
console.log(`[EXECUTE-CLAUDE] ALLOWED_TOOLS: ${process.env.ALLOWED_TOOLS || 'undefined'}`);
console.log(`[EXECUTE-CLAUDE] DISALLOWED_TOOLS: ${process.env.DISALLOWED_TOOLS || 'undefined'}`);
console.log(`[EXECUTE-CLAUDE] MCP_CONFIG length: ${process.env.MCP_CONFIG?.length || 0}`);
console.log(`[EXECUTE-CLAUDE] USE_BEDROCK: ${process.env.USE_BEDROCK}`);
console.log(`[EXECUTE-CLAUDE] USE_VERTEX: ${process.env.USE_VERTEX}`);
const config: ClaudeExecutorConfig = { const config: ClaudeExecutorConfig = {
apiKey: process.env.ANTHROPIC_API_KEY, apiKey: process.env.ANTHROPIC_API_KEY,
model: process.env.ANTHROPIC_MODEL || process.env.MODEL, model: process.env.ANTHROPIC_MODEL || process.env.MODEL,
@@ -23,7 +34,7 @@ async function main() {
useVertex: process.env.USE_VERTEX === "true", useVertex: process.env.USE_VERTEX === "true",
}; };
console.log("Starting Claude execution..."); console.log("[EXECUTE-CLAUDE] Configuration prepared, starting Claude execution...");
const result = await runClaude(config); const result = await runClaude(config);
// Set outputs for GitHub Actions // Set outputs for GitHub Actions

View File

@@ -6,6 +6,14 @@ export async function prepareMcpConfig(
repo: string, repo: string,
branch: string, branch: string,
): Promise<string> { ): Promise<string> {
console.log("[MCP-INSTALL] Preparing MCP configuration...");
console.log(`[MCP-INSTALL] Owner: ${owner}`);
console.log(`[MCP-INSTALL] Repo: ${repo}`);
console.log(`[MCP-INSTALL] Branch: ${branch}`);
console.log(`[MCP-INSTALL] GitHub token: ${githubToken ? '***' : 'undefined'}`);
console.log(`[MCP-INSTALL] GITHUB_ACTION_PATH: ${process.env.GITHUB_ACTION_PATH}`);
console.log(`[MCP-INSTALL] GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`);
try { try {
const mcpConfig = { const mcpConfig = {
mcpServers: { mcpServers: {
@@ -42,8 +50,14 @@ export async function prepareMcpConfig(
}, },
}; };
return JSON.stringify(mcpConfig, null, 2); const configString = JSON.stringify(mcpConfig, null, 2);
console.log("[MCP-INSTALL] Generated MCP configuration:");
console.log(configString);
console.log("[MCP-INSTALL] MCP config generation completed successfully");
return configString;
} catch (error) { } catch (error) {
console.error("[MCP-INSTALL] MCP config generation failed:", error);
core.setFailed(`Install MCP server failed with error: ${error}`); core.setFailed(`Install MCP server failed with error: ${error}`);
process.exit(1); process.exit(1);
} }

View File

@@ -15,9 +15,17 @@ const REPO_DIR = process.env.REPO_DIR || process.cwd();
const GITHUB_TOKEN = process.env.GITHUB_TOKEN; const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const GITEA_API_URL = process.env.GITEA_API_URL || "https://api.github.com"; const GITEA_API_URL = process.env.GITEA_API_URL || "https://api.github.com";
console.log(`[LOCAL-GIT-MCP] Starting Local Git Operations MCP Server`);
console.log(`[LOCAL-GIT-MCP] REPO_OWNER: ${REPO_OWNER}`);
console.log(`[LOCAL-GIT-MCP] REPO_NAME: ${REPO_NAME}`);
console.log(`[LOCAL-GIT-MCP] BRANCH_NAME: ${BRANCH_NAME}`);
console.log(`[LOCAL-GIT-MCP] REPO_DIR: ${REPO_DIR}`);
console.log(`[LOCAL-GIT-MCP] GITEA_API_URL: ${GITEA_API_URL}`);
console.log(`[LOCAL-GIT-MCP] GITHUB_TOKEN: ${GITHUB_TOKEN ? '***' : 'undefined'}`);
if (!REPO_OWNER || !REPO_NAME || !BRANCH_NAME) { if (!REPO_OWNER || !REPO_NAME || !BRANCH_NAME) {
console.error( console.error(
"Error: REPO_OWNER, REPO_NAME, and BRANCH_NAME environment variables are required", "[LOCAL-GIT-MCP] Error: REPO_OWNER, REPO_NAME, and BRANCH_NAME environment variables are required",
); );
process.exit(1); process.exit(1);
} }
@@ -30,19 +38,20 @@ const server = new McpServer({
// Helper function to run git commands // Helper function to run git commands
function runGitCommand(command: string): string { function runGitCommand(command: string): string {
try { try {
console.log(`Running git command: ${command}`); console.log(`[LOCAL-GIT-MCP] Running git command: ${command}`);
console.log(`[LOCAL-GIT-MCP] Working directory: ${REPO_DIR}`);
const result = execSync(command, { const result = execSync(command, {
cwd: REPO_DIR, cwd: REPO_DIR,
encoding: "utf8", encoding: "utf8",
stdio: ["inherit", "pipe", "pipe"], stdio: ["inherit", "pipe", "pipe"],
}); });
console.log(`Git command result: ${result.trim()}`); console.log(`[LOCAL-GIT-MCP] Git command result: ${result.trim()}`);
return result.trim(); return result.trim();
} catch (error: any) { } catch (error: any) {
console.error(`Git command failed: ${command}`); console.error(`[LOCAL-GIT-MCP] Git command failed: ${command}`);
console.error(`Error: ${error.message}`); console.error(`[LOCAL-GIT-MCP] Error: ${error.message}`);
if (error.stdout) console.error(`Stdout: ${error.stdout}`); if (error.stdout) console.error(`[LOCAL-GIT-MCP] Stdout: ${error.stdout}`);
if (error.stderr) console.error(`Stderr: ${error.stderr}`); if (error.stderr) console.error(`[LOCAL-GIT-MCP] Stderr: ${error.stderr}`);
throw error; throw error;
} }
} }
@@ -104,16 +113,21 @@ server.tool(
message: z.string().describe("Commit message"), message: z.string().describe("Commit message"),
}, },
async ({ files, message }) => { async ({ files, message }) => {
console.log(`[LOCAL-GIT-MCP] commit_files called with files: ${JSON.stringify(files)}, message: ${message}`);
try { try {
// Add the specified files // Add the specified files
console.log(`[LOCAL-GIT-MCP] Adding ${files.length} files to git...`);
for (const file of files) { for (const file of files) {
const filePath = file.startsWith("/") ? file.slice(1) : file; const filePath = file.startsWith("/") ? file.slice(1) : file;
console.log(`[LOCAL-GIT-MCP] Adding file: ${filePath}`);
runGitCommand(`git add "${filePath}"`); runGitCommand(`git add "${filePath}"`);
} }
// Commit the changes // Commit the changes
console.log(`[LOCAL-GIT-MCP] Committing with message: ${message}`);
runGitCommand(`git commit -m "${message}"`); runGitCommand(`git commit -m "${message}"`);
console.log(`[LOCAL-GIT-MCP] Successfully committed ${files.length} files`);
return { return {
content: [ content: [
{ {
@@ -125,6 +139,7 @@ server.tool(
} catch (error) { } catch (error) {
const errorMessage = const errorMessage =
error instanceof Error ? error.message : String(error); error instanceof Error ? error.message : String(error);
console.error(`[LOCAL-GIT-MCP] Error committing files: ${errorMessage}`);
return { return {
content: [ content: [
{ {
@@ -308,10 +323,14 @@ server.tool(
// Get git status tool // Get git status tool
server.tool("git_status", "Get the current git status", {}, async () => { server.tool("git_status", "Get the current git status", {}, async () => {
console.log(`[LOCAL-GIT-MCP] git_status called`);
try { try {
const status = runGitCommand("git status --porcelain"); const status = runGitCommand("git status --porcelain");
const currentBranch = runGitCommand("git rev-parse --abbrev-ref HEAD"); const currentBranch = runGitCommand("git rev-parse --abbrev-ref HEAD");
console.log(`[LOCAL-GIT-MCP] Current branch: ${currentBranch}`);
console.log(`[LOCAL-GIT-MCP] Git status: ${status || "Working tree clean"}`);
return { return {
content: [ content: [
{ {
@@ -322,6 +341,7 @@ server.tool("git_status", "Get the current git status", {}, async () => {
}; };
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error); const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`[LOCAL-GIT-MCP] Error getting git status: ${errorMessage}`);
return { return {
content: [ content: [
{ {
@@ -336,11 +356,19 @@ server.tool("git_status", "Get the current git status", {}, async () => {
}); });
async function runServer() { async function runServer() {
console.log(`[LOCAL-GIT-MCP] Starting MCP server transport...`);
const transport = new StdioServerTransport(); const transport = new StdioServerTransport();
console.log(`[LOCAL-GIT-MCP] Connecting to transport...`);
await server.connect(transport); await server.connect(transport);
console.log(`[LOCAL-GIT-MCP] MCP server connected and ready!`);
process.on("exit", () => { process.on("exit", () => {
console.log(`[LOCAL-GIT-MCP] Server shutting down...`);
server.close(); server.close();
}); });
} }
runServer().catch(console.error); console.log(`[LOCAL-GIT-MCP] Calling runServer()...`);
runServer().catch((error) => {
console.error(`[LOCAL-GIT-MCP] Server startup failed:`, error);
process.exit(1);
});