Skip to main content
Glama

MCP-REPL

by AnEntrypoint
MIT License
3,295
104
  • Linux
  • Apple
executor-tool.js27.7 kB
import { spawn, execSync } from 'child_process'; import { validateWorkingDirectory, createToolResponse, validateRequiredParams as validateRequiredParamsUtil } from '../core/utilities.js'; import { writeFileSync, chmodSync, unlinkSync } from 'fs'; import path from 'path'; import os from 'os'; import { workingDirectoryContext, createToolContext } from '../core/working-directory-context.js'; import { suppressConsoleOutput } from '../core/console-suppression.js'; import { executionState, shouldExecuteAsync, addExecutionStatusToResponse } from '../core/execution-state.js'; import { ToolError, ToolErrorHandler } from '../core/error-handling.js'; import { withConnectionManagement, getGlobalConnectionManager } from '../core/connection-manager.js'; import { withCrossToolAwareness, addToolMetadata } from '../core/cross-tool-context.js'; function getContextSummary(context) { if (!context || !context.sessionData) { return ''; } const lines = []; // Only include practically useful information if (context.workingDirectory !== process.cwd()) { lines.push(`Working directory: ${context.workingDirectory}`); } return lines.join('\n') + '\n'; } function createExecutionErrorResponse(error, startTime, context = {}) { return { success: false, error: error?.message || error || 'Unknown error occurred', executionTimeMs: Date.now() - startTime, ...context }; } // Enhance execution result with error analysis and actionable suggestions function enhanceExecutionResult(result, code, runtime, workingDirectory) { if (!result || result.success) { return result; } // Perform error analysis for syntax errors const errorAnalysis = analyzeExecutionError(result.stderr, result.stdout, runtime, code); // Create enhanced error response const enhancedResult = { ...result, _errorAnalysis: errorAnalysis, _requiresTroubleshooting: errorAnalysis.isSyntaxError || !result.success, _suggestions: errorAnalysis.suggestions || [], _confidence: errorAnalysis.confidence }; // If this is a syntax error, provide detailed guidance if (errorAnalysis.isSyntaxError) { enhancedResult.error = `🐛 SYNTAX ERROR DETECTED: ${errorAnalysis.specificError}`; enhancedResult._troubleshootingRequired = true; enhancedResult._nextSteps = [ 'Review the code syntax carefully', 'Fix the identified syntax issues', 'Test the corrected code again', 'Use language-specific documentation if needed' ]; } return enhancedResult; } function generateExecutionInsights(result, query, workingDirectory) { const insights = []; if (result.success) { if (result.filesAccessed && result.filesAccessed.length > 0) { insights.push(`Modified ${result.filesAccessed.length} files: ${result.filesAccessed.slice(0, 3).join(', ')}${result.filesAccessed.length > 3 ? '...' : ''}`); } if (result.executionTimeMs) { insights.push(`Execution time: ${result.executionTimeMs}ms`); } if (result.stdout && result.stdout.length > 0) { const lines = result.stdout.split('\n').length; insights.push(`Generated ${lines} lines of output`); } } if (workingDirectory !== process.cwd()) { insights.push(`Working directory: ${workingDirectory}`); } return insights; } function createSuccessResponse(data, startTime, context = {}) { return { success: true, executionTimeMs: Date.now() - startTime, ...data, ...context }; } function createTimeoutError(operation, timeoutMs, startTime) { return createExecutionErrorResponse( `${operation} timed out after ${timeoutMs}ms`, startTime, { timeout: true, timeoutMs } ); } function handleProcessError(error, command, startTime) { let errorMessage = 'Process execution failed'; let errorContext = { command }; let suggestions = []; if (error.code === 'ENOENT') { errorMessage = `Command not found: ${command}`; errorContext.missingCommand = true; suggestions = [ `Install ${command} using your package manager`, `Check if ${command} is in your PATH`, `Verify the command name is correct` ]; } else if (error.code === 'EACCES') { errorMessage = `Permission denied executing: ${command}`; errorContext.permissionDenied = true; suggestions = [ `Check file permissions for ${command}`, `Run with appropriate permissions`, `Verify the command is executable` ]; } else if (error.signal) { errorMessage = `Process terminated with signal: ${error.signal}`; errorContext.signal = error.signal; suggestions = [ `Process was killed by signal ${error.signal}`, `Check for resource limits or timeouts`, `Verify system resources are available` ]; } else if (error.code) { errorMessage = `Process failed with code: ${error.code}`; errorContext.exitCode = error.code; suggestions = [ `Check the error output for details`, `Verify the command syntax and arguments`, `Check if all required dependencies are available` ]; } return { ...createExecutionErrorResponse(errorMessage, startTime, errorContext), suggestions, requiresTroubleshooting: true }; } // Enhanced syntax error detection and analysis function analyzeExecutionError(stderr, stdout, runtime, code) { const analysis = { isSyntaxError: false, errorType: 'unknown', specificError: null, suggestions: [], confidence: 0, context: {} }; const errorOutput = (stderr || stdout || '').toLowerCase(); // JavaScript/TypeScript syntax errors if (['nodejs', 'deno'].includes(runtime)) { if (errorOutput.includes('syntaxerror')) { analysis.isSyntaxError = true; analysis.errorType = 'syntax'; analysis.specificError = extractJavaScriptError(errorOutput); analysis.confidence = 0.9; } } // Python syntax errors else if (runtime === 'python') { if (errorOutput.includes('syntaxerror')) { analysis.isSyntaxError = true; analysis.errorType = 'syntax'; analysis.specificError = extractPythonError(errorOutput); analysis.confidence = 0.9; } } // Go compilation errors else if (runtime === 'go') { if (errorOutput.includes('syntax error') || errorOutput.includes('expected')) { analysis.isSyntaxError = true; analysis.errorType = 'syntax'; analysis.specificError = extractGoError(errorOutput); analysis.confidence = 0.85; } } // Rust compilation errors else if (runtime === 'rust') { if (errorOutput.includes('error:') && errorOutput.includes('expected')) { analysis.isSyntaxError = true; analysis.errorType = 'syntax'; analysis.specificError = extractRustError(errorOutput); analysis.confidence = 0.85; } } // C/C++ compilation errors else if (['c', 'cpp'].includes(runtime)) { if (errorOutput.includes('error:') && errorOutput.includes('expected')) { analysis.isSyntaxError = true; analysis.errorType = 'syntax'; analysis.specificError = extractCppError(errorOutput); analysis.confidence = 0.85; } } return analysis; } function extractJavaScriptError(errorOutput) { const match = errorOutput.match(/syntaxerror:\s*(.+?)(?=\n|$)/i); return match ? match[1].trim() : 'Unknown JavaScript syntax error'; } function extractPythonError(errorOutput) { const match = errorOutput.match(/syntaxerror:\s*(.+?)(?=\n|$)/i); return match ? match[1].trim() : 'Unknown Python syntax error'; } function extractGoError(errorOutput) { const match = errorOutput.match(/syntax error:\s*(.+?)(?=\n|$)/i); return match ? match[1].trim() : 'Unknown Go syntax error'; } function extractRustError(errorOutput) { const match = errorOutput.match(/error\[E\d+\]:\s*(.+?)(?=\n|$)/i); return match ? match[1].trim() : 'Unknown Rust syntax error'; } function extractCppError(errorOutput) { const match = errorOutput.match(/error:\s*(.+?)(?=\n|$)/i); return match ? match[1].trim() : 'Unknown C/C++ syntax error'; } export async function executeProcess(command, args = [], options = {}) { const startTime = Date.now(); const { timeout = 240000, cwd, input, encoding = 'utf8' } = options; return new Promise((resolve) => { const child = spawn(command, args, { cwd, stdio: input ? 'pipe' : ['pipe', 'pipe', 'pipe'] }); let stdout = ''; let stderr = ''; let isResolved = false; const timeoutId = setTimeout(() => { if (!isResolved) { child.kill('SIGTERM'); isResolved = true; resolve(createTimeoutError(`${command} ${args.join(' ')}`, timeout, startTime)); } }, timeout); if (child.stdout) { child.stdout.on('data', (data) => { if (data && typeof data === 'object' && Buffer.isBuffer(data)) { stdout += data.toString(encoding); } else if (data && typeof data === 'string') { stdout += data; } }); } if (child.stderr) { child.stderr.on('data', (data) => { if (data && typeof data === 'object' && Buffer.isBuffer(data)) { stderr += data.toString(encoding); } else if (data && typeof data === 'string') { stderr += data; } }); } child.on('close', (code, signal) => { if (!isResolved) { clearTimeout(timeoutId); isResolved = true; if (code === 0) { resolve(createSuccessResponse({ stdout, stderr, code, signal }, startTime)); } else { resolve(createExecutionErrorResponse( stderr || `Process exited with code ${code}`, startTime, { stdout, stderr, code, signal } )); } } }); child.on('error', (error) => { if (!isResolved) { clearTimeout(timeoutId); isResolved = true; resolve(handleProcessError(error, `${command} ${args.join(' ')}`, startTime)); } }); if (input && child.stdin) { child.stdin.write(input); child.stdin.end(); } }); } const EXECUTION_CONFIGS = { nodejs: { command: 'node', args: ['-e'], description: 'Node.js JavaScript' }, deno: { command: 'deno', args: ['eval', '--no-check'], description: 'Deno JavaScript/TypeScript' }, bash: { command: 'bash', args: ['-c'], description: 'Bash shell commands' }, go: { command: 'go', args: ['run'], description: 'Go programming language', requiresFile: true }, rust: { command: 'rustc', args: [], description: 'Rust programming language', requiresCompile: true }, python: { command: 'python3', args: ['-c'], description: 'Python programming language' }, c: { command: 'gcc', args: [], description: 'C programming language', requiresCompile: true }, cpp: { command: 'g++', args: [], description: 'C++ programming language', requiresCompile: true } }; export async function executeWithRuntime(codeOrCommands, runtime, options = {}) { const { workingDirectory, timeout = 240000 } = options; const config = EXECUTION_CONFIGS[runtime]; if (!config) { throw new Error(`Unsupported runtime: ${runtime}`); } if (runtime === 'bash') { if (Array.isArray(codeOrCommands)) { const script = createBashScript(codeOrCommands); const tempScript = path.join(os.tmpdir(), `glootie_bash_${Date.now()}.sh`); writeFileSync(tempScript, script); chmodSync(tempScript, '755'); return executeProcess(config.command, [tempScript], { cwd: workingDirectory, timeout, encoding: 'utf8' }).finally(() => { try { unlinkSync(tempScript); } catch (e) { } }); } else { return executeProcess(config.command, [...config.args, codeOrCommands], { cwd: workingDirectory, timeout, encoding: 'utf8' }); } } if (config.requiresFile) { const tempFile = path.join(os.tmpdir(), `glootie_${runtime}_${Date.now()}.${runtime === 'go' ? 'go' : 'rs'}`); try { writeFileSync(tempFile, codeOrCommands); return executeProcess(config.command, [...config.args, tempFile], { cwd: workingDirectory, timeout, encoding: 'utf8' }); } finally { try { unlinkSync(tempFile); } catch (e) {} } } if (config.requiresCompile) { const extensions = { rust: 'rs', c: 'c', cpp: 'cpp' }; const compilers = { rust: 'rustc', c: 'gcc', cpp: 'g++' }; const tempFile = path.join(os.tmpdir(), `glootie_${runtime}_${Date.now()}.${extensions[runtime]}`); const tempExec = path.join(os.tmpdir(), `glootie_${runtime}_${Date.now()}`); try { writeFileSync(tempFile, codeOrCommands); const compileResult = await executeProcess(compilers[runtime], [tempFile, '-o', tempExec], { cwd: workingDirectory, timeout: timeout / 2, encoding: 'utf8' }); if (!compileResult.success) { return compileResult; } return await executeProcess(tempExec, [], { cwd: workingDirectory, timeout: timeout / 2, encoding: 'utf8' }); } finally { try { unlinkSync(tempFile); } catch (e) {} try { unlinkSync(tempExec); } catch (e) {} } } return executeProcess(config.command, [...config.args, codeOrCommands], { cwd: workingDirectory, timeout, encoding: 'utf8' }); } export async function executeBashCommands(commands, options = {}) { return executeWithRuntime(commands, 'bash', options); } export async function executeNodeCode(code, options = {}) { return executeWithRuntime(code, 'nodejs', options); } export async function executeDenoCode(code, options = {}) { return executeWithRuntime(code, 'deno', options); } export async function executeBashCommand(commands, timeout = 240000, workingDirectory, defaultWorkingDir) { const startTime = Date.now(); const paramError = validateRequiredParams({ workingDirectory }, ['workingDirectory'], startTime); if (paramError) return paramError; const dirValidation = validateWorkingDirectory(workingDirectory); if (!dirValidation.isValid) { return createExecutionErrorResponse(dirValidation.error, startTime); } const effectiveWorkingDir = dirValidation.resolvedDir; const commandArray = Array.isArray(commands) ? commands : [commands]; const nonEmptyCommands = commandArray.filter(cmd => { if (typeof cmd !== 'string') return false; const trimmed = cmd.trim(); return trimmed.length > 0 && !trimmed.startsWith('#'); }); if (nonEmptyCommands.length === 0) { return createExecutionErrorResponse("No valid commands to execute", startTime); } const validationResult = validateExecutionContent(nonEmptyCommands, 'Commands'); if (!validationResult.valid) { return createExecutionErrorResponse(validationResult.error, startTime); } const securityValidation = validateBashCommands(nonEmptyCommands); if (!securityValidation.valid) { return createExecutionErrorResponse(securityValidation.error, startTime); } const result = await executeBashCommands(nonEmptyCommands, { workingDirectory: dirValidation.effectiveDir, timeout }); return result; } export function validateBashCommands(commands) { if (!Array.isArray(commands) || commands.length === 0) { return { valid: false, error: "Commands must be a non-empty array" }; } for (let i = 0; i < commands.length; i++) { const command = commands[i]; if (typeof command !== 'string') { return { valid: false, error: `Command ${i}: Must be a string` }; } if (command.trim().length === 0) { return { valid: false, error: `Command ${i}: Cannot be empty or whitespace only` }; } const dangerous = ['rm -rf /', 'sudo rm', 'format', 'mkfs', ':(){ :|:& };:', 'dd if=/dev/zero']; const lowerCommand = command.toLowerCase(); for (const pattern of dangerous) { if (lowerCommand.includes(pattern)) { return { valid: false, error: `Command ${i}: Contains potentially dangerous pattern '${pattern}'` }; } } } return { valid: true }; } function createBashScript(commands) { const scriptLines = [ '#!/bin/bash', 'set -e', 'set -o pipefail', '', 'echo "=== BASH EXECUTION START ==="', `echo "Commands to execute: ${commands.length}"`, 'echo "Working directory: $(pwd)"', 'echo "Timestamp: $(date)"', 'echo ""' ]; commands.forEach((command, index) => { scriptLines.push(`echo "--- Command ${index + 1}/${commands.length} ---"`); scriptLines.push(`echo "$ ${command}"`); scriptLines.push(command); scriptLines.push('CMD_EXIT_CODE=$?'); scriptLines.push('if [ $CMD_EXIT_CODE -ne 0 ]; then'); scriptLines.push(` echo "Command ${index + 1} failed with exit code $CMD_EXIT_CODE" >&2`); scriptLines.push(' exit $CMD_EXIT_CODE'); scriptLines.push('fi'); scriptLines.push('echo ""'); }); scriptLines.push('echo "=== BASH EXECUTION COMPLETE ==="'); scriptLines.push('echo "All commands completed"'); return scriptLines.join('\n'); } // validateRequiredParamsUtil is now imported from utilities.js function validateExecutionContent(content, type) { if (!content || (typeof content !== 'string' && !Array.isArray(content))) { return { valid: false, error: `${type} must be a non-empty string or array` }; } if (typeof content === 'string' && content.trim().length === 0) { return { valid: false, error: `${type} cannot be empty or whitespace only` }; } if (Array.isArray(content) && content.length === 0) { return { valid: false, error: `${type} array cannot be empty` }; } if (Array.isArray(content)) { for (let i = 0; i < content.length; i++) { const item = content[i]; if (typeof item !== 'string') { return { valid: false, error: `${type} array item ${i} must be a string` }; } if (item.trim().length === 0) { return { valid: false, error: `${type} array item ${i} cannot be empty` }; } } } return { valid: true }; } let runtimeCache = null; export function detectAvailableRuntimes() { if (runtimeCache) return runtimeCache; const runtimes = { nodejs: { cmd: 'node --version', available: false, version: null }, deno: { cmd: 'deno --version', available: false, version: null }, bash: { cmd: 'bash --version', available: false, version: null }, go: { cmd: 'go version', available: false, version: null }, rust: { cmd: 'rustc --version', available: false, version: null }, python: { cmd: 'python3 --version', available: false, version: null }, c: { cmd: 'gcc --version', available: false, version: null }, cpp: { cmd: 'g++ --version', available: false, version: null } }; for (const [name, config] of Object.entries(runtimes)) { try { const result = execSync(config.cmd, { encoding: 'utf8', stdio: 'pipe' }); config.available = true; config.version = result.split('\n')[0].trim(); } catch (error) { config.available = false; } } runtimeCache = runtimes; return runtimes; } export async function executeWithRuntimeValidation(codeOrCommands, runtime, options = {}) { const startTime = Date.now(); const runtimes = detectAvailableRuntimes(); const runtimeInfo = runtimes[runtime]; if (!runtimeInfo || !runtimeInfo.available) { const config = EXECUTION_CONFIGS[runtime]; const errorMessage = `${config.description} runtime not available. Install ${config.command} to use this feature.`; return createExecutionErrorResponse(errorMessage, startTime); } return executeWithRuntime(codeOrCommands, runtime, options); } export async function executeGoCode(code, options = {}) { return executeWithRuntimeValidation(code, 'go', options); } export async function executePythonCode(code, options = {}) { return executeWithRuntimeValidation(code, 'python', options); } export async function executeRustCode(code, options = {}) { return executeWithRuntimeValidation(code, 'rust', options); } export async function executeCCode(code, options = {}) { return executeWithRuntimeValidation(code, 'c', options); } export async function executeCppCode(code, options = {}) { return executeWithRuntimeValidation(code, 'cpp', options); } export { generateExecutionInsights }; export const executionTools = [ { name: "execute", description: "Execute code in JS/TS, Go, Rust, Python, C, C++, or bash with auto-runtime detection. Primary tool for testing hypotheses before implementation. Supports both code snippets and shell commands.", inputSchema: { type: "object", properties: { workingDirectory: { type: "string", description: "Path to working directory for execution." }, code: { type: "string", description: "Code to execute. Include timeouts for network/async operations to prevent hangs." }, commands: { type: ["string", "array"], description: "Bash commands (single or array for planned batch executions)" }, runtime: { type: "string", enum: ["nodejs", "deno", "bash", "go", "rust", "python", "c", "cpp", "auto"], description: "Execution runtime (default: auto-detect)" }, timeout: { type: "number", description: "Timeout in milliseconds (default: 240000)" } }, required: ["workingDirectory"] }, handler: withCrossToolAwareness(withConnectionManagement(async ({ code, commands, workingDirectory, runtime = "auto", timeout = 240000 }) => { const consoleRestore = suppressConsoleOutput(); const effectiveWorkingDirectory = workingDirectory; const query = code || commands || ''; try { // Validate required parameters if (!workingDirectory) { throw new ToolError( 'Working directory is required', 'MISSING_PARAMETER', 'execute', false, ['Provide absolute path to working directory', 'Check tool documentation'] ); } // Start execution tracking for all operations const execution = executionState.startExecution({ type: 'execute', code, commands, runtime, workingDirectory: effectiveWorkingDirectory, timeout }); let result; if (code) { let targetRuntime = runtime === "auto" ? "nodejs" : runtime; if (runtime === "auto") { const shellCommands = ['npm ', 'npx ', 'yarn ', 'pip ', 'python ', 'go ', 'rustc ', 'gcc ', 'g++ ', 'git ', 'mkdir ', 'rm ', 'ls ', 'cd ']; const isShellCommand = shellCommands.some(cmd => code.trim().startsWith(cmd)); if (isShellCommand) { targetRuntime = 'bash'; } } try { const executionStart = Date.now(); result = await executeWithRuntimeValidation(code, targetRuntime, { workingDirectory, timeout }); const executionDuration = Date.now() - executionStart; // Complete execution (duration-based logic for cross-tool sharing) executionState.completeExecution(execution.id, result); } catch (executionError) { if (targetRuntime === 'bash') { try { result = await executeWithRuntimeValidation(code, 'nodejs', { workingDirectory, timeout }); } catch (fallbackError) { result = { success: false, stdout: '', stderr: `Failed to execute as both bash and nodejs:\nBash error: ${executionError.message}\nNode.js error: ${fallbackError.message}`, executionTimeMs: 0 }; } } else { result = { success: false, stdout: '', stderr: `Execution failed: ${executionError.message}`, executionTimeMs: 0 }; } executionState.failExecution(execution.id, executionError); } result = enhanceExecutionResult(result, code, targetRuntime, workingDirectory); } else if (commands) { try { const executionStart = Date.now(); result = await executeWithRuntimeValidation(commands, 'bash', { workingDirectory, timeout }); const executionDuration = Date.now() - executionStart; // Complete execution executionState.completeExecution(execution.id, result); } catch (executionError) { result = { success: false, stdout: '', stderr: `Command execution failed: ${executionError.message}`, executionTimeMs: 0 }; executionState.failExecution(execution.id, executionError); } result = enhanceExecutionResult(result, commands, 'bash', workingDirectory); } else { result = { content: [{ type: "text", text: "No code or commands provided" }] }; } const insights = generateExecutionInsights(result, query, effectiveWorkingDirectory); // Format response with enhanced error information let responseContent = result; if (result._errorAnalysis && result._errorAnalysis.isSyntaxError) { // Simple error response responseContent = { ...result, content: [{ type: "text", text: result.error || result.stderr || 'Execution failed' }] }; } else if (!result.success) { // Simple error format responseContent = { ...result, content: [{ type: "text", text: result.error || result.stderr || 'Execution failed' }] }; } else if (!result.content) { // Format successful execution or basic error let outputText = ''; if (result.success) { outputText = result.stdout || 'Execution successful'; } else { outputText = result.error || result.stderr || 'Execution failed'; } responseContent = { ...result, content: [{ type: "text", text: outputText }] }; } const toolContext = createToolContext('execute', effectiveWorkingDirectory, query, { ...responseContent, duration: responseContent.executionTimeMs || 0, filesAccessed: responseContent.filesAccessed || [], patterns: responseContent.patterns || [], insights: insights }); await workingDirectoryContext.updateContext(effectiveWorkingDirectory, 'execute', toolContext); // Clean up old executions executionState.cleanup(); return responseContent; } catch (error) { const errorContext = createToolContext('execute', effectiveWorkingDirectory, query, { error: error.message, duration: 0 }); await workingDirectoryContext.updateContext(effectiveWorkingDirectory, 'execute', errorContext); return { content: [{ type: "text", text: `Execution error: ${error.message}` }], isError: true }; } finally { consoleRestore.restore(); } }, 'execute', { maxRetries: 2, retryDelay: 1000 }), 'execute') } ];

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AnEntrypoint/mcp-repl'

If you have feedback or need assistance with the MCP directory API, please join our Discord server