UNPKG

requirements-analysis

Version:

简化的需求分析MCP服务 - 基于AI软件工程(优化版)6步流程

700 lines (599 loc) 20.6 kB
#!/usr/bin/env node /** * 简化需求分析MCP服务 * 基于AI软件工程(优化版)的6步需求分析流程 */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { ProjectInfo, AnalysisSession, MCPResponse, ProjectSummary, STEP_DEFINITIONS } from './types'; import { Logger } from './utils/logger'; import { Validator } from './utils/validator'; import { FileStorage } from './storage/file-storage'; import { Step1 } from './steps/step1'; import { Step2 } from './steps/step2'; import { Step3 } from './steps/step3'; import { Step4 } from './steps/step4'; import { Step5 } from './steps/step5'; import { Step6 } from './steps/step6'; class RequirementsAnalysisServer { private server: Server; private logger: Logger; private storage: FileStorage; private sessions: Map<string, AnalysisSession> = new Map(); constructor() { this.logger = new Logger(); // 获取输出目录配置,支持多种方式指定 const outputDir = this.getOutputDirectory(); const outputPath = require('path').join(outputDir, 'outputs'); this.logger.info('初始化文件存储', undefined, undefined, { outputDir: outputDir, outputPath: outputPath, cwd: process.cwd() }); this.storage = new FileStorage(outputPath, this.logger); this.server = new Server( { name: 'requirements-analysis-simple', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.setupHandlers(); } private setupHandlers(): void { // 列出可用工具 this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'start_requirements_analysis', description: '启动需求分析流程,基于AI软件工程(优化版)的6步标准流程', inputSchema: { type: 'object', properties: { projectName: { type: 'string', description: '项目名称' }, projectType: { type: 'string', enum: ['new', 'upgrade', 'integration', 'other'], description: '项目类型' }, industry: { type: 'string', description: '所属行业' }, background: { type: 'string', description: '项目背景' }, objectives: { type: 'string', description: '核心目标' }, budget: { type: 'string', description: '项目预算(可选)' }, timeline: { type: 'string', description: '项目周期(可选)' }, teamSize: { type: 'string', description: '团队规模(可选)' }, deployment: { type: 'string', enum: ['cloud', 'onpremise', 'hybrid'], description: '部署要求(可选)' }, specialRequirements: { type: 'string', description: '特殊要求(可选)' } }, required: ['projectName', 'projectType', 'industry', 'background', 'objectives'] } }, { name: 'execute_step', description: '🔥 重要:执行指定步骤并自动保存为MD文档。每次调用此工具都会将AI的执行结果保存为独立的MD文档文件,用于后续步骤的文档引用。这是核心功能,必须使用此工具才能保存过程文档!', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: '会话ID(从start_requirements_analysis获得)' }, stepNumber: { type: 'number', description: '步骤编号(1-6),每个步骤的结果都会保存为MD文档' }, result: { type: 'string', description: 'AI执行的完整步骤结果,将被保存为MD文档供后续步骤引用' } }, required: ['sessionId', 'stepNumber', 'result'] } }, { name: 'get_step_result', description: '获取指定步骤的结果', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: '会话ID' }, stepNumber: { type: 'number', description: '步骤编号(1-6)' } }, required: ['sessionId', 'stepNumber'] } }, { name: 'list_projects', description: '列出所有项目', inputSchema: { type: 'object', properties: {} } }, { name: 'check_document_status', description: '检查指定项目的文档保存状态', inputSchema: { type: 'object', properties: { projectName: { type: 'string', description: '项目名称' } }, required: ['projectName'] } }, { name: 'get_project_summary', description: '获取项目摘要', inputSchema: { type: 'object', properties: { sessionId: { type: 'string', description: '会话ID' } }, required: ['sessionId'] } } ] })); // 处理工具调用 this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { if (!args) { throw new Error('缺少参数'); } switch (name) { case 'start_requirements_analysis': return await this.startRequirementsAnalysis(args as any); case 'execute_step': const executeResult = await this.executeStep( (args as any).sessionId, (args as any).stepNumber, (args as any).result ); // 添加文档保存确认信息 if (executeResult.content && executeResult.content[0]) { const session = this.sessions.get((args as any).sessionId); const projectName = session?.projectInfo.projectName || '项目目录'; const originalText = executeResult.content[0].text; executeResult.content[0].text = originalText + `\n\n📁 **文档保存状态**: 步骤${(args as any).stepNumber}的结果已保存为MD文档到 outputs/${projectName}/step${(args as any).stepNumber}-*.md`; } return executeResult; case 'get_step_result': return await this.getStepResult( (args as any).sessionId, (args as any).stepNumber ); case 'list_projects': return await this.listProjects(); case 'check_document_status': return await this.checkDocumentStatus((args as any).projectName); case 'get_project_summary': return await this.getProjectSummary((args as any).sessionId); default: throw new Error(`未知工具: ${name}`); } } catch (error) { this.logger.error('工具调用失败', undefined, undefined, { tool: name, error: error instanceof Error ? error.message : String(error) }); return { content: [{ type: 'text', text: `❌ 错误: ${error instanceof Error ? error.message : String(error)}` }] }; } }); } /** * 启动需求分析 */ private async startRequirementsAnalysis(projectInfo: ProjectInfo): Promise<any> { // 参数验证 const errors = Validator.validateProjectInfo(projectInfo); if (errors.length > 0) { const errorMessage = Validator.formatValidationErrors(errors); return { content: [{ type: 'text', text: errorMessage }] }; } // 创建会话 const sessionId = this.generateSessionId(); const session: AnalysisSession = { sessionId, projectInfo, currentStep: 1, totalSteps: 6, stepResults: new Map(), startTime: new Date(), lastUpdateTime: new Date(), isComplete: false, outputDir: this.storage.getProjectDir(projectInfo.projectName) }; this.sessions.set(sessionId, session); this.logger.info('启动需求分析', sessionId, 1, { projectName: projectInfo.projectName, industry: projectInfo.industry }); // 生成第1步提示词 const step1Prompt = Step1.generatePrompt(projectInfo); // 保存项目摘要 await this.storage.saveProjectSummary(session); return { content: [{ type: 'text', text: `🚀 需求分析已启动! 📋 **项目信息** - 项目名称:${projectInfo.projectName} - 项目类型:${projectInfo.projectType} - 所属行业:${projectInfo.industry} 🎯 **分析流程** 基于AI软件工程(优化版)的6步标准流程: 1. ✅ 项目基础信息填写 (5分钟) 2. ⏳ AI智能分析项目全貌 (10分钟) 3. ⏳ 生成需求分析文档初版 (10分钟) 4. ⏳ AI质量分析 (8分钟) 5. ⏳ 生成改进建议 (5分钟) 6. ⏳ 生成最终文档 (10分钟) 📝 **第1步提示词**: \`\`\` ${step1Prompt} \`\`\` 🔥 **重要说明**: 本系统的核心功能是将每个步骤的AI执行结果保存为独立的MD文档,用于后续步骤的文档引用。 📁 **文档保存机制**: - 每次调用 \`execute_step\` 工具都会自动保存MD文档 - 文档保存路径:outputs/${projectInfo.projectName}/stepX-*.md - 后续步骤会自动引用前面步骤的文档内容 🔄 **下一步操作**: 请执行上述提示词,然后**必须**调用 \`execute_step\` 工具提交结果(这样才能保存文档): \`\`\`json { "sessionId": "${sessionId}", "stepNumber": 1, "result": "[AI执行的结果]" } \`\`\` 💾 **输出目录**:${session.outputDir} ⏱️ **预计总时间**:48分钟 🎯 **目标质量**:97分` }] }; } /** * 执行步骤 */ private async executeStep(sessionId: string, stepNumber: number, result: string): Promise<any> { const session = this.sessions.get(sessionId); if (!session) { throw new Error(`会话不存在: ${sessionId}`); } if (stepNumber !== session.currentStep) { throw new Error(`步骤不匹配,期望第${session.currentStep}步,收到第${stepNumber}步`); } // 创建步骤结果 let stepResult; switch (stepNumber) { case 1: stepResult = Step1.createStepResult(session.projectInfo, result); break; case 2: stepResult = await Step2.createStepResult(session, result); break; case 3: stepResult = await Step3.createStepResult(session, result); break; case 4: stepResult = await Step4.createStepResult(session, result); break; case 5: stepResult = await Step5.createStepResult(session, result); break; case 6: stepResult = await Step6.createStepResult(session, result); break; default: throw new Error(`无效的步骤编号: ${stepNumber}`); } // 保存步骤结果 session.stepResults.set(stepNumber, stepResult); session.lastUpdateTime = new Date(); // 保存到文件 await this.storage.saveStepResult(session, stepResult); // 更新项目摘要 await this.storage.saveProjectSummary(session); this.logger.info(`第${stepNumber}步执行完成`, sessionId, stepNumber); // 检查是否完成所有步骤 if (stepNumber === 6) { session.isComplete = true; return { content: [{ type: 'text', text: `🎉 需求分析已完成! 📊 **完成统计** - 项目名称:${session.projectInfo.projectName} - 总用时:${Math.round((Date.now() - session.startTime.getTime()) / 60000)}分钟 - 完成步骤:6/6 - 输出目录:${session.outputDir} 📁 **生成文档** - step1-项目信息.md - step2-AI分析.md - step3-需求文档初版.md - step4-质量分析.md - step5-改进建议.md - step6-最终文档.md - README.md (项目摘要) ✅ **质量目标达成**:97分高质量需求分析文档 🚀 **可直接用于**:产品设计阶段` }] }; } // 准备下一步 const nextStep = stepNumber + 1; session.currentStep = nextStep; let nextPrompt = ''; switch (nextStep) { case 2: nextPrompt = await Step2.generatePrompt(session); break; case 3: nextPrompt = await Step3.generatePrompt(session); break; case 4: nextPrompt = await Step4.generatePrompt(session); break; case 5: nextPrompt = await Step5.generatePrompt(session); break; case 6: nextPrompt = await Step6.generatePrompt(session); break; } const nextStepDef = STEP_DEFINITIONS[nextStep - 1]; return { content: [{ type: 'text', text: `✅ 第${stepNumber}步已完成并保存 📋 **当前进度** - 已完成:${stepNumber}/6 步骤 - 进度:${Math.round((stepNumber / 6) * 100)}% 🔄 **第${nextStep}步:${nextStepDef.name}** 预计用时:${nextStepDef.estimatedTime}分钟 📝 **执行提示词**: \`\`\` ${nextPrompt} \`\`\` 🔄 **下一步操作**: 执行上述提示词后,**必须**调用 \`execute_step\` 工具提交结果(这样才能保存为MD文档): \`\`\`json { "sessionId": "${sessionId}", "stepNumber": ${nextStep}, "result": "[AI执行的结果]" } \`\`\`` }] }; } /** * 获取步骤结果 */ private async getStepResult(sessionId: string, stepNumber: number): Promise<any> { const session = this.sessions.get(sessionId); if (!session) { throw new Error(`会话不存在: ${sessionId}`); } const stepResult = session.stepResults.get(stepNumber); if (!stepResult) { return { content: [{ type: 'text', text: `第${stepNumber}步尚未执行` }] }; } return { content: [{ type: 'text', text: `# 第${stepNumber}步结果 **步骤名称**: ${stepResult.stepName} **执行时间**: ${stepResult.timestamp.toLocaleString('zh-CN')} **文件路径**: ${stepResult.filePath} ## 执行结果 ${stepResult.result}` }] }; } /** * 列出所有项目 */ private async listProjects(): Promise<any> { const projects = await this.storage.listProjects(); if (projects.length === 0) { return { content: [{ type: 'text', text: '📁 暂无项目' }] }; } const projectList = projects.map((project, index) => `${index + 1}. ${project}` ).join('\n'); return { content: [{ type: 'text', text: `📁 项目列表 (${projects.length}个) ${projectList} 💡 使用 \`get_project_summary\` 查看项目详情` }] }; } /** * 检查文档保存状态 */ private async checkDocumentStatus(projectName: string): Promise<any> { try { const projectDir = this.storage.getProjectDir(projectName); const files = await require('fs').promises.readdir(projectDir); const stepFiles = files.filter((f: string) => f.startsWith('step') && f.endsWith('.md')); const stepNumbers = stepFiles.map((f: string) => { const match = f.match(/step(\d+)-/); return match ? parseInt(match[1]) : 0; }).filter((n: number) => n > 0).sort((a: number, b: number) => a - b); const fileDetails = await Promise.all(stepFiles.map(async (file: string) => { const filePath = require('path').join(projectDir, file); const stats = await require('fs').promises.stat(filePath); return { fileName: file, size: stats.size, lastModified: stats.mtime.toLocaleString('zh-CN') }; })); return { content: [{ type: 'text' as const, text: `📁 **项目文档状态检查** **项目名称**: ${projectName} **文档目录**: ${projectDir} **已保存的步骤文档**: ${stepNumbers.map((n: number) => `✅ 第${n}步`).join('\n')} **文档详情**: ${fileDetails.map(f => `📄 ${f.fileName} (${f.size}字节, 修改时间: ${f.lastModified})`).join('\n')} **总计**: ${stepFiles.length}个文档文件 ${stepNumbers.length === 0 ? '⚠️ 未找到任何步骤文档' : '✅ 文档保存正常'}` }] }; } catch (error) { return { content: [{ type: 'text' as const, text: `❌ **检查文档状态失败** **项目名称**: ${projectName} **错误信息**: ${error instanceof Error ? error.message : String(error)} 可能的原因: 1. 项目目录不存在 2. 还没有执行任何步骤 3. 文件权限问题 建议: 1. 确认项目名称正确 2. 先使用start_requirements_analysis工具启动项目 3. 使用execute_step工具执行步骤` }] }; } } /** * 获取项目摘要 */ private async getProjectSummary(sessionId: string): Promise<any> { const session = this.sessions.get(sessionId); if (!session) { throw new Error(`会话不存在: ${sessionId}`); } const completedSteps = Array.from(session.stepResults.keys()).sort(); const progress = Math.round((completedSteps.length / session.totalSteps) * 100); return { content: [{ type: 'text', text: `📊 项目摘要 **项目名称**: ${session.projectInfo.projectName} **所属行业**: ${session.projectInfo.industry} **开始时间**: ${session.startTime.toLocaleString('zh-CN')} **最后更新**: ${session.lastUpdateTime.toLocaleString('zh-CN')} **完成进度**: ${progress}% (${completedSteps.length}/${session.totalSteps}) **状态**: ${session.isComplete ? '已完成' : '进行中'} **输出目录**: ${session.outputDir} **已完成步骤**: ${completedSteps.join(', ')}` }] }; } /** * 获取输出目录 */ private getOutputDirectory(): string { // 优先级:命令行参数 > 环境变量 > 当前工作目录 // 1. 检查命令行参数 const args = process.argv; const outputArgIndex = args.findIndex(arg => arg === '--output-dir' || arg === '-o'); if (outputArgIndex !== -1 && args[outputArgIndex + 1]) { return args[outputArgIndex + 1]; } // 2. 检查环境变量 if (process.env.REQUIREMENTS_OUTPUT_DIR) { return process.env.REQUIREMENTS_OUTPUT_DIR; } // 3. 使用当前工作目录 return process.cwd(); } /** * 生成会话ID */ private generateSessionId(): string { return Date.now().toString(36) + Math.random().toString(36).substr(2); } /** * 启动服务 */ async run(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); this.logger.info('需求分析MCP服务已启动', undefined, undefined, { name: 'requirements-analysis-simple', version: '1.0.0', steps: 6 }); } /** * 测试专用:启动需求分析并返回会话ID */ public async startRequirementsAnalysisForTest(projectInfo: ProjectInfo): Promise<{ sessionId: string; session: AnalysisSession }> { // 参数验证 const errors = Validator.validateProjectInfo(projectInfo); if (errors.length > 0) { throw new Error(Validator.formatValidationErrors(errors)); } // 创建会话 const sessionId = this.generateSessionId(); const session: AnalysisSession = { sessionId, projectInfo, currentStep: 1, totalSteps: 6, stepResults: new Map(), startTime: new Date(), lastUpdateTime: new Date(), isComplete: false, outputDir: this.storage.getProjectDir(projectInfo.projectName) }; this.sessions.set(sessionId, session); this.logger.info('启动需求分析(测试模式)', sessionId, 1, { projectName: projectInfo.projectName, industry: projectInfo.industry }); // 保存项目摘要 await this.storage.saveProjectSummary(session); return { sessionId, session }; } } // 导出类供测试使用 export { RequirementsAnalysisServer }; // 启动服务 if (require.main === module) { const server = new RequirementsAnalysisServer(); server.run().catch(console.error); }