claude-expert-workflow-mcp
Version:
Production-ready MCP server for AI-powered product development consultation through specialized expert roles. Enterprise-grade with memory management, monitoring, and Claude Code integration.
263 lines (224 loc) • 7.29 kB
text/typescript
import {
WorkflowSession,
WorkflowType,
WorkflowState,
ExpertType,
ExpertOutput,
WorkflowOptions,
WorkflowProgress
} from '@/types/workflow';
import { conversationManager } from '@/state/conversationManager';
import { logger } from '@/utils/logger';
export class WorkflowEngine {
private workflows: Map<string, WorkflowSession> = new Map();
// Default linear workflow: PM -> UX -> Architect
private readonly DEFAULT_LINEAR_QUEUE: ExpertType[] = [
'product_manager',
'ux_designer',
'software_architect'
];
/**
* Generate a unique workflow session ID
*/
private generateWorkflowId(): string {
return `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Start a new workflow session
*/
startWorkflow(
projectDescription: string,
options: WorkflowOptions = {}
): string {
const workflowId = this.generateWorkflowId();
const { workflowType = 'linear', customExpertQueue } = options;
let expertQueue: ExpertType[];
switch (workflowType) {
case 'linear':
expertQueue = [...this.DEFAULT_LINEAR_QUEUE];
break;
case 'parallel':
// For parallel, all experts work simultaneously
expertQueue = [...this.DEFAULT_LINEAR_QUEUE];
break;
case 'custom':
expertQueue = customExpertQueue || [...this.DEFAULT_LINEAR_QUEUE];
break;
default:
expertQueue = [...this.DEFAULT_LINEAR_QUEUE];
}
const workflow: WorkflowSession = {
id: workflowId,
projectDescription,
workflowType,
expertQueue,
currentExpert: null,
state: 'initialized',
outputs: [],
createdAt: new Date(),
updatedAt: new Date()
};
this.workflows.set(workflowId, workflow);
logger.debug(`Started workflow ${workflowId} with type ${workflowType}`);
// Automatically start with the first expert for linear workflows
if (workflowType === 'linear' && expertQueue.length > 0) {
this.progressWorkflow(workflowId);
}
return workflowId;
}
/**
* Progress the workflow to the next step
*/
progressWorkflow(workflowId: string): boolean {
const workflow = this.workflows.get(workflowId);
if (!workflow) {
logger.error(`Workflow ${workflowId} not found`);
return false;
}
try {
switch (workflow.state) {
case 'initialized':
return this._initializeWorkflow(workflow);
case 'in_progress':
return this._continueWorkflow(workflow);
case 'expert_consultation':
// Wait for current expert consultation to complete
return true;
case 'completed':
case 'failed':
logger.info(`Workflow ${workflowId} already in final state: ${workflow.state}`);
return false;
default:
logger.error(`Unknown workflow state: ${workflow.state}`);
return false;
}
} catch (error) {
return this._handleWorkflowError(workflow, error as Error);
}
}
/**
* Get current workflow status
*/
getWorkflowStatus(workflowId: string): WorkflowProgress | undefined {
const workflow = this.workflows.get(workflowId);
if (!workflow) {
return undefined;
}
const completedExperts = workflow.outputs.map(output => output.expertType);
const currentStep = completedExperts.length + (workflow.currentExpert ? 1 : 0);
return {
sessionId: workflow.id,
currentStep,
totalSteps: workflow.expertQueue.length,
currentExpert: workflow.currentExpert,
completedExperts,
state: workflow.state,
lastActivity: workflow.updatedAt
};
}
/**
* Add expert output and continue workflow
*/
addExpertOutput(
workflowId: string,
expertType: ExpertType,
conversationId: string,
output: string,
topics: string[]
): boolean {
const workflow = this.workflows.get(workflowId);
if (!workflow) {
logger.error(`Workflow ${workflowId} not found`);
return false;
}
if (workflow.currentExpert !== expertType) {
logger.error(`Expected expert ${workflow.currentExpert}, got ${expertType}`);
return false;
}
const expertOutput: ExpertOutput = {
expertType,
conversationId,
output,
completedAt: new Date(),
topics
};
workflow.outputs.push(expertOutput);
workflow.currentExpert = null;
workflow.state = 'in_progress';
workflow.updatedAt = new Date();
logger.debug(`Added output from ${expertType} to workflow ${workflowId}`);
// Continue to next expert or complete workflow
return this.progressWorkflow(workflowId);
}
/**
* Get all workflow outputs
*/
getWorkflowOutputs(workflowId: string): ExpertOutput[] {
const workflow = this.workflows.get(workflowId);
return workflow ? [...workflow.outputs] : [];
}
/**
* Get workflow session
*/
getWorkflowSession(workflowId: string): WorkflowSession | null {
const workflow = this.workflows.get(workflowId);
return workflow ? { ...workflow } : null;
}
/**
* List all active workflows
*/
getActiveWorkflows(): WorkflowProgress[] {
const activeWorkflows: WorkflowProgress[] = [];
for (const workflow of this.workflows.values()) {
if (workflow.state !== 'completed' && workflow.state !== 'failed') {
const status = this.getWorkflowStatus(workflow.id);
if (status) {
activeWorkflows.push(status);
}
}
}
return activeWorkflows;
}
// Private helper methods
private _initializeWorkflow(workflow: WorkflowSession): boolean {
if (workflow.expertQueue.length === 0) {
workflow.state = 'completed';
workflow.completedAt = new Date();
workflow.updatedAt = new Date();
return true;
}
const firstExpert = workflow.expertQueue[0];
workflow.currentExpert = firstExpert;
workflow.state = 'expert_consultation';
workflow.updatedAt = new Date();
logger.info(`Workflow ${workflow.id} starting with expert: ${firstExpert}`);
return true;
}
private _continueWorkflow(workflow: WorkflowSession): boolean {
const completedCount = workflow.outputs.length;
if (completedCount >= workflow.expertQueue.length) {
// All experts completed
workflow.state = 'completed';
workflow.completedAt = new Date();
workflow.updatedAt = new Date();
logger.info(`Workflow ${workflow.id} completed with ${completedCount} expert outputs`);
return true;
}
// Move to next expert
const nextExpert = workflow.expertQueue[completedCount];
workflow.currentExpert = nextExpert;
workflow.state = 'expert_consultation';
workflow.updatedAt = new Date();
logger.info(`Workflow ${workflow.id} progressing to expert: ${nextExpert}`);
return true;
}
private _handleWorkflowError(workflow: WorkflowSession, error: Error): boolean {
workflow.state = 'failed';
workflow.error = error.message;
workflow.currentExpert = null;
workflow.updatedAt = new Date();
logger.error(`Workflow ${workflow.id} failed: ${error.message}`);
return false;
}
}
export const workflowEngine = new WorkflowEngine();