From 8de76049e1e0e8746cdc6cb05efc828e254769a2 Mon Sep 17 00:00:00 2001 From: Mark Wylde Date: Fri, 30 May 2025 23:55:03 +0100 Subject: [PATCH] Attempt to make this work --- src/claude/executor.ts | 103 ++++++++++++++++++++++-------- src/entrypoints/execute-claude.ts | 13 +++- src/mcp/install-mcp-server.ts | 16 ++++- src/mcp/local-git-ops-server.ts | 44 ++++++++++--- 4 files changed, 138 insertions(+), 38 deletions(-) diff --git a/src/claude/executor.ts b/src/claude/executor.ts index 0c8f193..657a9fc 100644 --- a/src/claude/executor.ts +++ b/src/claude/executor.ts @@ -2,6 +2,8 @@ import Anthropic from "@anthropic-ai/sdk"; import * as fs from "fs"; +import { spawn } from "child_process"; +import { promisify } from "util"; export interface ClaudeExecutorConfig { apiKey?: string; @@ -23,43 +25,43 @@ export interface ClaudeExecutorResult { error?: string; } +interface MCPServer { + command: string; + args: string[]; + env?: Record; +} + +interface MCPConfig { + mcpServers: Record; +} + export class ClaudeExecutor { private config: ClaudeExecutorConfig; - private anthropic?: Anthropic; constructor(config: ClaudeExecutorConfig) { 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 { + console.log("[CLAUDE-EXECUTOR] Reading prompt..."); + if (this.config.prompt) { + console.log("[CLAUDE-EXECUTOR] Using direct prompt"); return this.config.prompt; } if (this.config.promptFile) { + console.log(`[CLAUDE-EXECUTOR] Using prompt file: ${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}`); } - 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"); } @@ -95,23 +97,63 @@ export class ClaudeExecutor { return logFile; } + private async setupMCPConfig(): Promise { + 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 { try { const prompt = await this.readPrompt(); const tools = this.parseTools(); + console.log(`[CLAUDE-EXECUTOR] Prompt length: ${prompt.length} characters`); 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(`Disallowed tools: ${tools.disallowed.join(", ") || "none"}`); + console.log(`[CLAUDE-EXECUTOR] Allowed tools: ${tools.allowed.join(", ") || "none"}`); + console.log(`[CLAUDE-EXECUTOR] Disallowed tools: ${tools.disallowed.join(", ") || "none"}`); - if (!this.anthropic) { - throw new Error("Anthropic client not initialized"); + // For now, just log that we're using the simple API approach + 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) + "..."); } - // Create a simple message request - const response = await this.anthropic.messages.create({ + 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 + const response = await anthropic.messages.create({ model: this.config.model || "claude-3-7-sonnet-20250219", max_tokens: 8192, 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); @@ -131,7 +178,7 @@ export class ClaudeExecutor { executionFile, }; } catch (error) { - console.error("Claude execution failed:", error); + console.error("[CLAUDE-EXECUTOR] Claude execution failed:", error); const executionFile = this.createExecutionLog(null, String(error)); diff --git a/src/entrypoints/execute-claude.ts b/src/entrypoints/execute-claude.ts index e85c605..09a0239 100644 --- a/src/entrypoints/execute-claude.ts +++ b/src/entrypoints/execute-claude.ts @@ -5,6 +5,17 @@ import { runClaude, type ClaudeExecutorConfig } from "../claude/executor"; async function main() { 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 = { apiKey: process.env.ANTHROPIC_API_KEY, model: process.env.ANTHROPIC_MODEL || process.env.MODEL, @@ -23,7 +34,7 @@ async function main() { 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); // Set outputs for GitHub Actions diff --git a/src/mcp/install-mcp-server.ts b/src/mcp/install-mcp-server.ts index c3ba023..a0ef194 100644 --- a/src/mcp/install-mcp-server.ts +++ b/src/mcp/install-mcp-server.ts @@ -6,6 +6,14 @@ export async function prepareMcpConfig( repo: string, branch: string, ): Promise { + 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 { const mcpConfig = { 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) { + console.error("[MCP-INSTALL] MCP config generation failed:", error); core.setFailed(`Install MCP server failed with error: ${error}`); process.exit(1); } diff --git a/src/mcp/local-git-ops-server.ts b/src/mcp/local-git-ops-server.ts index ed285a3..72c6104 100644 --- a/src/mcp/local-git-ops-server.ts +++ b/src/mcp/local-git-ops-server.ts @@ -15,9 +15,17 @@ const REPO_DIR = process.env.REPO_DIR || process.cwd(); const GITHUB_TOKEN = process.env.GITHUB_TOKEN; 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) { 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); } @@ -30,19 +38,20 @@ const server = new McpServer({ // Helper function to run git commands function runGitCommand(command: string): string { 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, { cwd: REPO_DIR, encoding: "utf8", stdio: ["inherit", "pipe", "pipe"], }); - console.log(`Git command result: ${result.trim()}`); + console.log(`[LOCAL-GIT-MCP] Git command result: ${result.trim()}`); return result.trim(); } catch (error: any) { - console.error(`Git command failed: ${command}`); - console.error(`Error: ${error.message}`); - if (error.stdout) console.error(`Stdout: ${error.stdout}`); - if (error.stderr) console.error(`Stderr: ${error.stderr}`); + console.error(`[LOCAL-GIT-MCP] Git command failed: ${command}`); + console.error(`[LOCAL-GIT-MCP] Error: ${error.message}`); + if (error.stdout) console.error(`[LOCAL-GIT-MCP] Stdout: ${error.stdout}`); + if (error.stderr) console.error(`[LOCAL-GIT-MCP] Stderr: ${error.stderr}`); throw error; } } @@ -104,16 +113,21 @@ server.tool( message: z.string().describe("Commit message"), }, async ({ files, message }) => { + console.log(`[LOCAL-GIT-MCP] commit_files called with files: ${JSON.stringify(files)}, message: ${message}`); try { // Add the specified files + console.log(`[LOCAL-GIT-MCP] Adding ${files.length} files to git...`); for (const file of files) { const filePath = file.startsWith("/") ? file.slice(1) : file; + console.log(`[LOCAL-GIT-MCP] Adding file: ${filePath}`); runGitCommand(`git add "${filePath}"`); } // Commit the changes + console.log(`[LOCAL-GIT-MCP] Committing with message: ${message}`); runGitCommand(`git commit -m "${message}"`); + console.log(`[LOCAL-GIT-MCP] Successfully committed ${files.length} files`); return { content: [ { @@ -125,6 +139,7 @@ server.tool( } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`[LOCAL-GIT-MCP] Error committing files: ${errorMessage}`); return { content: [ { @@ -308,10 +323,14 @@ server.tool( // Get git status tool server.tool("git_status", "Get the current git status", {}, async () => { + console.log(`[LOCAL-GIT-MCP] git_status called`); try { const status = runGitCommand("git status --porcelain"); 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 { content: [ { @@ -322,6 +341,7 @@ server.tool("git_status", "Get the current git status", {}, async () => { }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`[LOCAL-GIT-MCP] Error getting git status: ${errorMessage}`); return { content: [ { @@ -336,11 +356,19 @@ server.tool("git_status", "Get the current git status", {}, async () => { }); async function runServer() { + console.log(`[LOCAL-GIT-MCP] Starting MCP server transport...`); const transport = new StdioServerTransport(); + console.log(`[LOCAL-GIT-MCP] Connecting to transport...`); await server.connect(transport); + console.log(`[LOCAL-GIT-MCP] MCP server connected and ready!`); process.on("exit", () => { + console.log(`[LOCAL-GIT-MCP] Server shutting down...`); 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); +});