mcp-repl
Version:
MCP REPL with code execution, semantic code search, and comprehensive ast-grep integration
148 lines (131 loc) • 4.37 kB
JavaScript
// Batch executor for chaining multiple MCP tool calls
// Follows the project's convention over configuration and error propagation principles
/**
* Executes multiple MCP tool operations in sequence
* @param {Array} operations - Array of {tool, parameters} objects
* @param {Object} toolHandlers - Map of tool names to handler functions
* @param {string} workingDir - Default working directory for operations
* @returns {Object} Consolidated batch execution results
*/
export async function executeBatch(operations, toolHandlers, workingDir) {
const startTime = Date.now();
// Validate operations array
if (!Array.isArray(operations) || operations.length === 0) {
return {
success: false,
error: "Operations must be a non-empty array",
executionTimeMs: Date.now() - startTime
};
}
// Validate operations structure
for (let i = 0; i < operations.length; i++) {
const op = operations[i];
if (!op.tool || typeof op.tool !== 'string') {
return {
success: false,
error: `Operation ${i}: Missing or invalid tool name`,
executionTimeMs: Date.now() - startTime
};
}
if (!toolHandlers[op.tool]) {
return {
success: false,
error: `Operation ${i}: Unknown tool '${op.tool}'`,
executionTimeMs: Date.now() - startTime
};
}
if (!op.arguments || typeof op.arguments !== 'object') {
return {
success: false,
error: `Operation ${i}: Missing or invalid arguments object`,
executionTimeMs: Date.now() - startTime
};
}
}
const results = [];
let successfulOperations = 0;
let failedOperations = 0;
// Execute each operation in sequence
for (let i = 0; i < operations.length; i++) {
const operation = operations[i];
const operationStartTime = Date.now();
try {
// Prepare parameters with working directory fallback
const params = {
...operation.arguments,
workingDirectory: operation.arguments.workingDirectory || workingDir
};
// Execute the tool
const handler = toolHandlers[operation.tool];
const result = await handler(params);
const operationResult = {
operation: i,
tool: operation.tool,
success: true,
content: result.content,
executionTimeMs: Date.now() - operationStartTime
};
results.push(operationResult);
successfulOperations++;
} catch (error) {
const operationResult = {
operation: i,
tool: operation.tool,
success: false,
error: error.message,
executionTimeMs: Date.now() - operationStartTime
};
results.push(operationResult);
failedOperations++;
}
}
return {
success: true,
totalOperations: operations.length,
successfulOperations,
failedOperations,
executionTimeMs: Date.now() - startTime,
results
};
}
/**
* Validates batch operation parameters according to tool requirements
* @param {Array} operations - Operations to validate
* @returns {Object} Validation result
*/
export function validateBatchOperations(operations) {
const toolRequirements = {
executenodejs: ['code'],
executedeno: ['code'],
executebash: [],
searchcode: ['query'],
astgrep_search: ['pattern'],
astgrep_replace: ['pattern', 'replacement'],
astgrep_lint: ['rules'],
astgrep_analyze: ['pattern'],
astgrep_enhanced_search: ['pattern'],
astgrep_multi_pattern: ['patterns'],
astgrep_constraint_search: ['pattern'],
astgrep_project_init: ['language'],
astgrep_project_scan: [],
astgrep_test: ['pattern'],
astgrep_validate_rules: ['rules'],
astgrep_debug_rule: ['pattern'],
sequentialthinking: ['thoughts']
};
for (let i = 0; i < operations.length; i++) {
const op = operations[i];
const requirements = toolRequirements[op.tool];
if (requirements) {
for (const req of requirements) {
if (!op.arguments[req]) {
return {
valid: false,
error: `Operation ${i} (${op.tool}): Missing required parameter '${req}'`
};
}
}
}
}
return { valid: true };
}