requirements-analysis
Version:
简化的需求分析MCP服务 - 基于AI软件工程(优化版)6步流程
700 lines (599 loc) • 20.6 kB
text/typescript
#!/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);
}