UNPKG

mcp-workflow-server-enhanced

Version:

Enhanced MCP Workflow Server with smart problem routing, comprehensive validation, guide compliance, and robust error handling. Intelligently routes to appropriate AI functions based on problem type.

522 lines (452 loc) 15.2 kB
import { ImplementationInputSchema, FunctionOutput, WorkflowContext, } from '../shared/types.js'; import { logWorkflowProgress } from '../shared/utils.js'; /** * Implementation Function * * This function executes the generated tasks according to the implementation plan. * It processes tasks sequentially or in parallel, validates completion criteria, * and provides detailed progress tracking and error handling. */ export function createImplementationFunction() { return async (input: any, context: WorkflowContext): Promise<FunctionOutput> => { try { // Validate input const validatedInput = ImplementationInputSchema.parse(input); const { tasks, currentTaskIndex } = validatedInput; logWorkflowProgress(context, 'implementation', 'Starting task implementation phase'); if (!tasks || tasks.length === 0) { throw new Error('No tasks provided for implementation'); } // Execute tasks based on current index const executionResult = await executeTasks(tasks, currentTaskIndex); // Validate task completion const validationResults = await validateTaskCompletion(executionResult.completedTasks); // Generate progress report const progressReport = generateProgressReport(tasks, executionResult); // Check for any issues or blockers const issuesAnalysis = analyzeIssues(executionResult, validationResults); // Determine next steps const nextSteps = determineNextSteps(executionResult, tasks); const result = { executionSummary: executionResult, validationResults, progressReport, issuesAnalysis, nextSteps, completedTasks: executionResult.completedTasks, pendingTasks: executionResult.pendingTasks, failedTasks: executionResult.failedTasks, overallStatus: calculateOverallStatus(executionResult), recommendations: generateRecommendations(executionResult, issuesAnalysis), }; logWorkflowProgress(context, 'implementation', 'Implementation phase completed'); return { success: true, result, nextStep: executionResult.hasErrors ? 'problem-solver' : undefined, context, metadata: { tasksCompleted: executionResult.completedTasks.length, tasksFailed: executionResult.failedTasks.length, overallProgress: progressReport.overallProgress, processingTime: Date.now(), }, }; } catch (error) { return { success: false, result: { error: error.message, step: 'implementation', }, context, }; } }; } /** * Execute tasks according to the implementation plan */ async function executeTasks( tasks: any[], startIndex: number = 0 ): Promise<{ completedTasks: any[]; pendingTasks: any[]; failedTasks: any[]; executionLog: any[]; hasErrors: boolean; totalExecutionTime: number; }> { const completedTasks = []; const pendingTasks = []; const failedTasks = []; const executionLog = []; const startTime = Date.now(); for (let i = startIndex; i < tasks.length; i++) { const task = tasks[i]; try { logWorkflowProgress({} as any, 'implementation', `Executing task: ${task.name}`); // Check dependencies const dependenciesResult = await checkTaskDependencies(task, completedTasks); if (!dependenciesResult.satisfied) { pendingTasks.push({ ...task, reason: `Dependencies not satisfied: ${dependenciesResult.missing.join(', ')}`, }); continue; } // Execute the task const taskResult = await executeIndividualTask(task); if (taskResult.success) { completedTasks.push({ ...task, executionResult: taskResult, completedAt: new Date().toISOString(), }); executionLog.push({ taskId: task.id, status: 'completed', duration: taskResult.duration, timestamp: new Date().toISOString(), }); } else { failedTasks.push({ ...task, error: taskResult.error, failedAt: new Date().toISOString(), }); executionLog.push({ taskId: task.id, status: 'failed', error: taskResult.error, timestamp: new Date().toISOString(), }); } } catch (error) { failedTasks.push({ ...task, error: error.message, failedAt: new Date().toISOString(), }); executionLog.push({ taskId: task.id, status: 'failed', error: error.message, timestamp: new Date().toISOString(), }); } } // Add remaining tasks as pending for (let i = startIndex + completedTasks.length + failedTasks.length; i < tasks.length; i++) { pendingTasks.push(tasks[i]); } return { completedTasks, pendingTasks, failedTasks, executionLog, hasErrors: failedTasks.length > 0, totalExecutionTime: Date.now() - startTime, }; } /** * Check if task dependencies are satisfied */ async function checkTaskDependencies( task: any, completedTasks: any[] ): Promise<{ satisfied: boolean; missing: string[] }> { if (!task.dependencies || task.dependencies.length === 0) { return { satisfied: true, missing: [] }; } const completedTaskNames = completedTasks.map(t => t.name); const missing = task.dependencies.filter(dep => !completedTaskNames.includes(dep)); return { satisfied: missing.length === 0, missing, }; } /** * Execute an individual task */ async function executeIndividualTask(task: any): Promise<{ success: boolean; duration: number; output?: any; error?: string; validationResults?: any; }> { const startTime = Date.now(); try { // Simulate task execution based on task type const executionResult = await simulateTaskExecution(task); // Validate task completion const validationResult = await validateTaskExecution(task, executionResult); if (!validationResult.valid) { return { success: false, duration: Date.now() - startTime, error: `Validation failed: ${validationResult.issues.join(', ')}`, }; } return { success: true, duration: Date.now() - startTime, output: executionResult, validationResults: validationResult, }; } catch (error) { return { success: false, duration: Date.now() - startTime, error: error.message, }; } } /** * Simulate task execution (in a real implementation, this would perform actual work) */ async function simulateTaskExecution(task: any): Promise<any> { // Simulate execution time based on task effort const effortHours = parseFloat(task.effort?.match(/(\d+)/)?.[1] || '1'); const simulationTime = Math.min(effortHours * 100, 2000); // Max 2 seconds simulation await new Promise(resolve => setTimeout(resolve, simulationTime)); // Generate simulated output based on task type const output = { taskId: task.id, taskName: task.name, executedSteps: task.implementationSteps || [], deliverables: task.deliverables || [], timestamp: new Date().toISOString(), }; // Add specific outputs based on task type if (task.name.includes('Setup') || task.name.includes('Configuration')) { output.configurationFiles = ['package.json', 'tsconfig.json', 'eslint.config.js']; output.dependenciesInstalled = true; } else if (task.name.includes('Development') || task.name.includes('Implementation')) { output.codeFiles = ['src/main.ts', 'src/utils.ts']; output.functionsImplemented = ['mainFunction', 'helperFunction']; } else if (task.name.includes('Testing')) { output.testFiles = ['tests/main.test.ts', 'tests/utils.test.ts']; output.testResults = { passed: 10, failed: 0, coverage: '95%' }; } else if (task.name.includes('Documentation')) { output.documentationFiles = ['README.md', 'API.md']; output.examplesCreated = true; } return output; } /** * Validate task execution against acceptance criteria */ async function validateTaskExecution(task: any, executionResult: any): Promise<{ valid: boolean; issues: string[]; checkedCriteria: string[]; }> { const issues = []; const checkedCriteria = []; // Check acceptance criteria if (task.acceptanceCriteria) { for (const criteria of task.acceptanceCriteria) { checkedCriteria.push(criteria); // Simulate validation logic if (criteria.includes('implementation matches requirements')) { if (!executionResult.functionsImplemented && !executionResult.configurationFiles) { issues.push('Implementation does not match requirements'); } } if (criteria.includes('tests pass')) { if (executionResult.testResults && executionResult.testResults.failed > 0) { issues.push('Some tests are failing'); } } if (criteria.includes('documentation')) { if (!executionResult.documentationFiles && !executionResult.examplesCreated) { issues.push('Documentation requirements not met'); } } } } // Check deliverables if (task.deliverables) { for (const deliverable of task.deliverables) { if (!executionResult.deliverables.includes(deliverable)) { issues.push(`Missing deliverable: ${deliverable}`); } } } return { valid: issues.length === 0, issues, checkedCriteria, }; } /** * Validate completion of all tasks */ async function validateTaskCompletion(completedTasks: any[]): Promise<{ overallValid: boolean; taskValidations: any[]; summary: any; }> { const taskValidations = []; let totalIssues = 0; for (const task of completedTasks) { const validation = task.executionResult?.validationResults || { valid: true, issues: [], checkedCriteria: [], }; taskValidations.push({ taskId: task.id, taskName: task.name, valid: validation.valid, issues: validation.issues, checkedCriteria: validation.checkedCriteria, }); totalIssues += validation.issues.length; } return { overallValid: totalIssues === 0, taskValidations, summary: { totalTasks: completedTasks.length, validTasks: taskValidations.filter(v => v.valid).length, totalIssues, }, }; } /** * Generate progress report */ function generateProgressReport(allTasks: any[], executionResult: any): { overallProgress: number; phaseProgress: { [key: string]: number }; taskBreakdown: any; timeAnalysis: any; } { const totalTasks = allTasks.length; const completedCount = executionResult.completedTasks.length; const overallProgress = Math.round((completedCount / totalTasks) * 100); // Calculate progress by phase const phaseProgress = {}; const tasksByPhase = allTasks.reduce((groups, task) => { if (!groups[task.phase]) groups[task.phase] = []; groups[task.phase].push(task); return groups; }, {}); for (const [phase, tasks] of Object.entries(tasksByPhase)) { const phaseCompleted = executionResult.completedTasks.filter(t => t.phase === phase).length; phaseProgress[phase] = Math.round((phaseCompleted / (tasks as any[]).length) * 100); } return { overallProgress, phaseProgress, taskBreakdown: { total: totalTasks, completed: completedCount, failed: executionResult.failedTasks.length, pending: executionResult.pendingTasks.length, }, timeAnalysis: { totalExecutionTime: executionResult.totalExecutionTime, averageTaskTime: completedCount > 0 ? executionResult.totalExecutionTime / completedCount : 0, }, }; } /** * Analyze issues and blockers */ function analyzeIssues(executionResult: any, validationResults: any): { criticalIssues: string[]; warnings: string[]; blockers: string[]; recommendations: string[]; } { const criticalIssues = []; const warnings = []; const blockers = []; const recommendations = []; // Analyze failed tasks for (const failedTask of executionResult.failedTasks) { if (failedTask.priority === 'high') { criticalIssues.push(`High priority task failed: ${failedTask.name} - ${failedTask.error}`); blockers.push(`${failedTask.name} failure blocks dependent tasks`); } else { warnings.push(`Task failed: ${failedTask.name} - ${failedTask.error}`); } } // Analyze validation issues for (const validation of validationResults.taskValidations) { if (!validation.valid) { warnings.push(`Validation issues in ${validation.taskName}: ${validation.issues.join(', ')}`); } } // Generate recommendations if (executionResult.failedTasks.length > 0) { recommendations.push('Review and fix failed tasks before proceeding'); } if (executionResult.pendingTasks.length > 0) { recommendations.push('Address dependency issues for pending tasks'); } if (validationResults.summary.totalIssues > 0) { recommendations.push('Resolve validation issues to ensure quality'); } return { criticalIssues, warnings, blockers, recommendations, }; } /** * Determine next steps based on execution results */ function determineNextSteps(executionResult: any, allTasks: any[]): string[] { const nextSteps = []; if (executionResult.failedTasks.length > 0) { nextSteps.push('Investigate and resolve failed tasks'); nextSteps.push('Consider using problem-solver function for complex issues'); } if (executionResult.pendingTasks.length > 0) { nextSteps.push('Address dependency issues for pending tasks'); nextSteps.push('Continue implementation with remaining tasks'); } if (executionResult.completedTasks.length === allTasks.length) { nextSteps.push('All tasks completed - proceed to final validation'); nextSteps.push('Prepare for deployment and documentation'); } return nextSteps; } /** * Calculate overall implementation status */ function calculateOverallStatus(executionResult: any): 'completed' | 'in-progress' | 'failed' | 'blocked' { if (executionResult.failedTasks.length > 0) { const highPriorityFailed = executionResult.failedTasks.some(t => t.priority === 'high'); return highPriorityFailed ? 'blocked' : 'failed'; } if (executionResult.pendingTasks.length > 0) { return 'in-progress'; } return 'completed'; } /** * Generate recommendations based on execution results */ function generateRecommendations(executionResult: any, issuesAnalysis: any): string[] { const recommendations = []; recommendations.push(...issuesAnalysis.recommendations); if (executionResult.hasErrors) { recommendations.push('Use problem-solver function to address implementation issues'); } if (executionResult.completedTasks.length > 0) { recommendations.push('Document lessons learned from completed tasks'); } return [...new Set(recommendations)]; // Remove duplicates }