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.
475 lines • 22.6 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ConsultExpertSchema, GeneratePRDSchema, GenerateDesignSpecSchema, GenerateTechArchitectureSchema, StartWorkflowSchema, ProgressWorkflowSchema, GetWorkflowStatusSchema, GenerateIntegratedDocumentSchema, AddWorkflowExpertOutputSchema } from '../types';
import { productManagerExpert } from '../experts/productManager';
import { uxDesignerExpert } from '../experts/uxDesigner';
import { softwareArchitectExpert } from '../experts/softwareArchitect';
import { conversationManager } from '../state/conversationManager';
import { claudeClient } from '../claude/client';
import { generatePRD } from '../templates/prd';
import { generateDesignSpec } from '../templates/designSpec';
import { generateTechArchitecture } from '../templates/techArchitecture';
import { logger } from '../utils/logger';
// Import orchestration components
import { workflowEngine } from '../orchestration/workflowEngine';
import { expertOrchestrator } from '../orchestration/expertOrchestrator';
import { integratedDocumentGenerator } from '../orchestration/integratedDocumentGenerator';
export class ClaudeExpertMCPServer {
constructor() {
this.server = new Server({
name: 'claude-expert-workflow',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
this.setupToolHandlers();
}
setupToolHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// Single expert consultation tools
{
name: 'consultProductManager',
description: 'Consult with AI Product Manager expert for product planning and requirements',
inputSchema: ConsultExpertSchema,
},
{
name: 'consultUXDesigner',
description: 'Consult with AI UX Designer expert for user experience and interface design',
inputSchema: ConsultExpertSchema,
},
{
name: 'consultSoftwareArchitect',
description: 'Consult with AI Software Architect expert for technical architecture and system design',
inputSchema: ConsultExpertSchema,
},
// Document generation tools
{
name: 'generatePRD',
description: 'Generate Product Requirements Document from conversation',
inputSchema: GeneratePRDSchema,
},
{
name: 'generateDesignSpec',
description: 'Generate Design Specification Document from conversation',
inputSchema: GenerateDesignSpecSchema,
},
{
name: 'generateTechArchitecture',
description: 'Generate Technical Architecture Document from conversation',
inputSchema: GenerateTechArchitectureSchema,
},
// Orchestration workflow tools
{
name: 'startWorkflow',
description: 'Initialize multi-expert workflow with project description and configuration',
inputSchema: StartWorkflowSchema,
},
{
name: 'progressWorkflow',
description: 'Continue workflow to next expert in the sequence',
inputSchema: ProgressWorkflowSchema,
},
{
name: 'getWorkflowStatus',
description: 'Get current workflow state, progress, and completed experts',
inputSchema: GetWorkflowStatusSchema,
},
{
name: 'generateIntegratedDocument',
description: 'Create master document combining all expert outputs with cross-references',
inputSchema: GenerateIntegratedDocumentSchema,
},
{
name: 'addWorkflowExpertOutput',
description: 'Add expert consultation result to workflow for tracking',
inputSchema: AddWorkflowExpertOutputSchema,
},
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
// Single expert tools
case 'consultProductManager':
return await this.handleConsultProductManager(args);
case 'consultUXDesigner':
return await this.handleConsultUXDesigner(args);
case 'consultSoftwareArchitect':
return await this.handleConsultSoftwareArchitect(args);
// Document generation tools
case 'generatePRD':
return await this.handleGeneratePRD(args);
case 'generateDesignSpec':
return await this.handleGenerateDesignSpec(args);
case 'generateTechArchitecture':
return await this.handleGenerateTechArchitecture(args);
// Orchestration tools
case 'startWorkflow':
return await this.handleStartWorkflow(args);
case 'progressWorkflow':
return await this.handleProgressWorkflow(args);
case 'getWorkflowStatus':
return await this.handleGetWorkflowStatus(args);
case 'generateIntegratedDocument':
return await this.handleGenerateIntegratedDocument(args);
case 'addWorkflowExpertOutput':
return await this.handleAddWorkflowExpertOutput(args);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
logger.error(`Tool ${name} error:`, error);
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
});
}
// ===== SINGLE EXPERT CONSULTATION HANDLERS =====
async handleConsultProductManager(args) {
const input = ConsultExpertSchema.parse(args);
// Get or create conversation
const conversationId = input.conversationId || conversationManager.createConversation();
// Add user message to conversation
conversationManager.addMessage(conversationId, 'user', input.projectInfo);
// Get conversation history for context
const history = conversationManager.getConversationHistory(conversationId);
const claudeMessages = history.slice(0, -1).map(msg => ({
role: msg.role,
content: msg.content
}));
// Consult with Product Manager expert
const response = await claudeClient.consultExpert(productManagerExpert.systemPrompt, input.projectInfo, claudeMessages);
// Add expert response to conversation
conversationManager.addMessage(conversationId, 'assistant', response);
return {
content: [
{
type: 'text',
text: response,
},
],
isError: false,
_meta: {
conversationId,
expert: 'Product Manager'
}
};
}
async handleConsultUXDesigner(args) {
const input = ConsultExpertSchema.parse(args);
// Get or create conversation
const conversationId = input.conversationId || conversationManager.createConversation();
// Add user message to conversation
conversationManager.addMessage(conversationId, 'user', input.projectInfo);
// Get conversation history for context
const history = conversationManager.getConversationHistory(conversationId);
const claudeMessages = history.slice(0, -1).map(msg => ({
role: msg.role,
content: msg.content
}));
// Consult with UX Designer expert
const response = await claudeClient.consultExpert(uxDesignerExpert.systemPrompt, input.projectInfo, claudeMessages);
// Add expert response to conversation
conversationManager.addMessage(conversationId, 'assistant', response);
return {
content: [
{
type: 'text',
text: response,
},
],
isError: false,
_meta: {
conversationId,
expert: 'UX Designer'
}
};
}
async handleConsultSoftwareArchitect(args) {
const input = ConsultExpertSchema.parse(args);
// Get or create conversation
const conversationId = input.conversationId || conversationManager.createConversation();
// Add user message to conversation
conversationManager.addMessage(conversationId, 'user', input.projectInfo);
// Get conversation history for context
const history = conversationManager.getConversationHistory(conversationId);
const claudeMessages = history.slice(0, -1).map(msg => ({
role: msg.role,
content: msg.content
}));
// Consult with Software Architect expert
const response = await claudeClient.consultExpert(softwareArchitectExpert.systemPrompt, input.projectInfo, claudeMessages);
// Add expert response to conversation
conversationManager.addMessage(conversationId, 'assistant', response);
return {
content: [
{
type: 'text',
text: response,
},
],
isError: false,
_meta: {
conversationId,
expert: 'Software Architect'
}
};
}
// ===== DOCUMENT GENERATION HANDLERS =====
async handleGeneratePRD(args) {
const input = GeneratePRDSchema.parse(args);
const conversation = conversationManager.getConversation(input.conversationId);
if (!conversation) {
throw new Error(`Conversation ${input.conversationId} not found`);
}
// Generate PRD from conversation history
const prd = await generatePRD(conversation, input.projectName);
return {
content: [
{
type: 'text',
text: prd,
},
],
isError: false,
_meta: {
conversationId: input.conversationId,
documentType: 'PRD'
}
};
}
async handleGenerateDesignSpec(args) {
const input = GenerateDesignSpecSchema.parse(args);
const conversation = conversationManager.getConversation(input.conversationId);
if (!conversation) {
throw new Error(`Conversation ${input.conversationId} not found`);
}
// Generate Design Specification from conversation history
const designSpec = await generateDesignSpec(conversation, input.projectName);
return {
content: [
{
type: 'text',
text: designSpec,
},
],
isError: false,
_meta: {
conversationId: input.conversationId,
documentType: 'Design Specification'
}
};
}
async handleGenerateTechArchitecture(args) {
const input = GenerateTechArchitectureSchema.parse(args);
const conversation = conversationManager.getConversation(input.conversationId);
if (!conversation) {
throw new Error(`Conversation ${input.conversationId} not found`);
}
// Generate Technical Architecture Document from conversation history
const techArchitecture = await generateTechArchitecture(conversation, input.projectName);
return {
content: [
{
type: 'text',
text: techArchitecture,
},
],
isError: false,
_meta: {
conversationId: input.conversationId,
documentType: 'Technical Architecture'
}
};
}
// ===== ORCHESTRATION WORKFLOW HANDLERS =====
async handleStartWorkflow(args) {
const input = StartWorkflowSchema.parse(args);
try {
const workflowOptions = {
workflowType: input.workflowType,
customExpertQueue: input.expertList
};
// Start the workflow
const workflowId = workflowEngine.startWorkflow(input.projectDescription, workflowOptions);
// Get the initial status
const status = workflowEngine.getWorkflowStatus(workflowId);
return {
content: [
{
type: 'text',
text: `Workflow started successfully!\n\nWorkflow ID: ${workflowId}\nProject: ${input.projectDescription}\nWorkflow Type: ${input.workflowType}\nCurrent Expert: ${status?.currentExpert || 'None'}\nState: ${status?.state}\n\nNext Steps:\n- Use 'progressWorkflow' to advance to the first expert\n- Use 'getWorkflowStatus' to check progress\n- Consult with experts using individual consultation tools\n- Use 'addWorkflowExpertOutput' to add expert results to the workflow`,
},
],
isError: false,
_meta: {
workflowId,
status
}
};
}
catch (error) {
logger.error('Error starting workflow:', error);
throw new Error(`Failed to start workflow: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async handleProgressWorkflow(args) {
const input = ProgressWorkflowSchema.parse(args);
try {
const success = workflowEngine.progressWorkflow(input.workflowId);
if (!success) {
throw new Error(`Failed to progress workflow ${input.workflowId}`);
}
const status = workflowEngine.getWorkflowStatus(input.workflowId);
if (!status) {
throw new Error(`Workflow ${input.workflowId} not found after progression`);
}
return {
content: [
{
type: 'text',
text: `Workflow progressed successfully!\n\nWorkflow ID: ${input.workflowId}\nCurrent Expert: ${status.currentExpert || 'All experts completed'}\nProgress: ${status.currentStep}/${status.totalSteps}\nState: ${status.state}\nCompleted Experts: [${status.completedExperts.join(', ')}]\n\nNext Steps:\n${status.currentExpert ? `- Consult with ${status.currentExpert.replace('_', ' ')} expert\n- Use 'addWorkflowExpertOutput' to add the consultation result` : '- All experts completed! Use \'generateIntegratedDocument\' to create the master document'}`,
},
],
isError: false,
_meta: {
workflowId: input.workflowId,
status
}
};
}
catch (error) {
logger.error('Error progressing workflow:', error);
throw new Error(`Failed to progress workflow: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async handleGetWorkflowStatus(args) {
const input = GetWorkflowStatusSchema.parse(args);
const status = workflowEngine.getWorkflowStatus(input.workflowId);
if (!status) {
throw new Error(`Workflow ${input.workflowId} not found`);
}
const workflow = workflowEngine.getWorkflowSession(input.workflowId);
return {
content: [
{
type: 'text',
text: `Workflow Status Report\n\n` +
`Workflow ID: ${status.sessionId}\n` +
`State: ${status.state}\n` +
`Progress: ${status.currentStep}/${status.totalSteps}\n` +
`Current Expert: ${status.currentExpert || 'None'}\n` +
`Completed Experts: [${status.completedExperts.join(', ')}]\n` +
`Last Activity: ${status.lastActivity.toISOString()}\n\n` +
`Project Description:\n${workflow?.projectDescription}\n\n` +
`Expert Outputs: ${workflow?.outputs.length || 0} completed\n` +
`${workflow?.outputs.map(output => `- ${output.expertType}: ${output.completedAt.toISOString()}`).join('\n') || ''}`,
},
],
isError: false,
_meta: {
workflowId: input.workflowId,
status,
workflow
}
};
}
async handleGenerateIntegratedDocument(args) {
const input = GenerateIntegratedDocumentSchema.parse(args);
try {
const workflow = workflowEngine.getWorkflowSession(input.workflowId);
if (!workflow) {
throw new Error(`Workflow ${input.workflowId} not found`);
}
if (workflow.outputs.length === 0) {
throw new Error('No expert outputs available for document generation');
}
// Generate integrated document
const masterDocument = await integratedDocumentGenerator.generateMasterDocument(workflow, {
includeCrossReferences: input.includeCrossReferences,
includeExecutiveSummary: input.includeExecutiveSummary,
includeDetailedSections: input.includeDetailedSections
});
return {
content: [
{
type: 'text',
text: masterDocument,
},
],
isError: false,
_meta: {
workflowId: input.workflowId,
documentType: 'Integrated Project Document',
expertOutputCount: workflow.outputs.length
}
};
}
catch (error) {
logger.error('Error generating integrated document:', error);
throw new Error(`Failed to generate integrated document: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async handleAddWorkflowExpertOutput(args) {
const input = AddWorkflowExpertOutputSchema.parse(args);
try {
const conversation = conversationManager.getConversation(input.conversationId);
if (!conversation) {
throw new Error(`Conversation ${input.conversationId} not found`);
}
// Get the expert's final output from the conversation
const expertMessages = conversation.messages.filter(msg => msg.role === 'assistant');
if (expertMessages.length === 0) {
throw new Error('No expert responses found in conversation');
}
const finalOutput = expertMessages[expertMessages.length - 1].content;
// Add output to workflow using expert orchestrator
const success = await expertOrchestrator.completeExpertConsultation(input.workflowId, input.expertType, input.conversationId, finalOutput);
if (!success) {
throw new Error('Failed to add expert output to workflow');
}
const status = workflowEngine.getWorkflowStatus(input.workflowId);
return {
content: [
{
type: 'text',
text: `Expert output added successfully!\n\n` +
`Workflow ID: ${input.workflowId}\n` +
`Expert: ${input.expertType.replace('_', ' ')}\n` +
`Conversation ID: ${input.conversationId}\n` +
`Current State: ${status?.state}\n\n` +
`Output Summary:\n${finalOutput.substring(0, 200)}${finalOutput.length > 200 ? '...' : ''}\n\n` +
`Next Steps:\n${status?.currentExpert ? `- Continue with next expert: ${status.currentExpert.replace('_', ' ')}\n- Use 'progressWorkflow' to advance` : '- All experts completed! Use \'generateIntegratedDocument\' to create master document'}`,
},
],
isError: false,
_meta: {
workflowId: input.workflowId,
expertType: input.expertType,
conversationId: input.conversationId,
status
}
};
}
catch (error) {
logger.error('Error adding expert output to workflow:', error);
throw new Error(`Failed to add expert output: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
logger.info('Claude Expert Workflow MCP server started with orchestration capabilities');
}
}
export const mcpServer = new ClaudeExpertMCPServer();
//# sourceMappingURL=server.js.map