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
text/typescript
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
}