mcp-swagger-server/packages/mcp-swagger-api/docs/nestjs-technical-specificat...

12 KiB
Raw Blame History

NestJS 实施技术规范

🎯 项目规格

基本信息

  • 项目名称: MCP Swagger API Server
  • 技术栈: NestJS + TypeScript + pnpm
  • 运行环境: Node.js 18+
  • 端口配置: 3001 (API服务) + 3322 (MCP协议)
  • 开发模式: Monorepo集成

依赖版本规范

{
  "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "@nestjs/config": "^3.0.0",
    "@nestjs/swagger": "^7.0.0",
    "@nestjs/terminus": "^10.0.0",
    "class-validator": "^0.14.0",
    "class-transformer": "^0.5.1",
    "cors": "^2.8.5",
    "helmet": "^7.0.0",
    "compression": "^1.7.4"
  },
  "devDependencies": {
    "@nestjs/cli": "^10.0.0",
    "@nestjs/testing": "^10.0.0",
    "@types/express": "^4.17.17",
    "jest": "^29.5.0",
    "supertest": "^6.3.0"
  }
}

🏗️ 架构设计规范

模块化架构

// 核心模块结构
@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: '.env',
    }),
    MCPModule,
    OpenAPIModule,
    HealthModule,
  ],
  controllers: [],
  providers: [
    {
      provide: APP_GUARD,
      useClass: MCPSessionGuard,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
    {
      provide: APP_FILTER,
      useClass: MCPExceptionFilter,
    },
  ],
})
export class AppModule {}

服务层设计模式

// 服务接口定义
export interface IMCPService {
  handleRequest(request: MCPRequest, sessionId?: string): Promise<MCPResponse>;
  getServerStatus(): Promise<ServerStatus>;
  configureDynamicTools(config: ToolConfiguration): Promise<ConfigResult>;
}

// 服务实现
@Injectable()
export class MCPService implements IMCPService {
  constructor(
    private readonly dynamicServerService: DynamicServerService,
    private readonly logger: Logger
  ) {}
  
  async handleRequest(request: MCPRequest, sessionId?: string): Promise<MCPResponse> {
    // 实现细节
  }
}

📋 API 接口规范

RESTful API 设计

1. OpenAPI 配置接口

@ApiTags('OpenAPI Configuration')
@Controller('api/openapi')
export class OpenAPIController {
  
  @Post('configure')
  @ApiOperation({ summary: '配置OpenAPI规范' })
  @ApiBody({ type: ConfigureOpenAPIDto })
  @ApiResponse({ status: 200, type: ConfigureResultDto })
  async configure(@Body() dto: ConfigureOpenAPIDto): Promise<ApiResponse<ConfigureResultDto>> {
    // 实现逻辑
  }
  
  @Get('status')
  @ApiOperation({ summary: '获取配置状态' })
  @ApiResponse({ status: 200, type: ConfigStatusDto })
  async getStatus(): Promise<ApiResponse<ConfigStatusDto>> {
    // 实现逻辑
  }
  
  @Get('tools')
  @ApiOperation({ summary: '获取工具列表' })
  @ApiResponse({ status: 200, type: ToolListDto })
  async getTools(): Promise<ApiResponse<ToolListDto>> {
    // 实现逻辑
  }
}

2. MCP 协议接口

@ApiTags('MCP Protocol')
@Controller('mcp')
@UseGuards(MCPSessionGuard)
export class MCPController {
  
  @Post()
  @ApiOperation({ summary: '处理MCP协议请求' })
  @ApiBody({ type: MCPRequestDto })
  @ApiResponse({ status: 200, type: MCPResponseDto })
  async handleMCP(
    @Body() request: MCPRequestDto,
    @Headers('mcp-session-id') sessionId?: string
  ): Promise<MCPResponseDto> {
    // 实现逻辑
  }
}

DTO 验证规范

// 请求DTO
export class ConfigureOpenAPIDto {
  @ApiProperty({ description: '输入源配置' })
  @IsObject()
  @ValidateNested()
  @Type(() => InputSourceDto)
  source: InputSourceDto;
  
  @ApiProperty({ description: '基础URL', required: false })
  @IsOptional()
  @IsUrl()
  baseUrl?: string;
  
  @ApiProperty({ description: '配置选项', required: false })
  @IsOptional()
  @IsObject()
  options?: ConfigurationOptions;
}

// 响应DTO
export class ConfigureResultDto {
  @ApiProperty({ description: 'API信息' })
  apiInfo: ApiInfo;
  
  @ApiProperty({ description: '端点列表' })
  endpoints: ApiEndpoint[];
  
  @ApiProperty({ description: '工具数量' })
  @IsNumber()
  toolsCount: number;
  
  @ApiProperty({ description: 'MCP服务器URL' })
  @IsUrl()
  mcpServerUrl: string;
  
  @ApiProperty({ description: '配置时间' })
  @IsDateString()
  configuredAt: string;
}

🔒 安全规范

认证与授权

// JWT 认证策略(可选)
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get<string>('JWT_SECRET'),
    });
  }
  
  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

// API密钥验证推荐
@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private configService: ConfigService) {}
  
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const apiKey = request.headers['x-api-key'];
    const validApiKey = this.configService.get<string>('API_KEY');
    
    return !validApiKey || apiKey === validApiKey;
  }
}

CORS 和安全中间件

// main.ts 安全配置
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // CORS配置
  app.enableCors({
    origin: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:5173'],
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-api-key', 'mcp-session-id'],
  });
  
  // 安全中间件
  app.use(helmet());
  app.use(compression());
  
  // 全局前缀
  app.setGlobalPrefix('api');
  
  // 全局验证管道
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
    })
  );
  
  await app.listen(3001);
}

📊 监控与日志规范

结构化日志

@Injectable()
export class LoggerService {
  private readonly logger = new Logger(LoggerService.name);
  
  logRequest(request: any, response: any, duration: number) {
    this.logger.log({
      type: 'request',
      method: request.method,
      url: request.url,
      statusCode: response.statusCode,
      duration,
      userAgent: request.headers['user-agent'],
      ip: request.ip,
    });
  }
  
  logError(error: any, context?: string) {
    this.logger.error({
      type: 'error',
      message: error.message,
      stack: error.stack,
      context,
    });
  }
}

健康检查

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private http: HttpHealthIndicator,
    private memory: MemoryHealthIndicator,
  ) {}
  
  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      () => this.http.pingCheck('mcp-server', 'http://localhost:3322/health'),
      () => this.memory.checkHeap('memory_heap', 150 * 1024 * 1024),
      () => this.memory.checkRSS('memory_rss', 150 * 1024 * 1024),
    ]);
  }
}

性能监控

@Injectable()
export class MetricsInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const start = Date.now();
    const request = context.switchToHttp().getRequest();
    
    return next.handle().pipe(
      tap(() => {
        const duration = Date.now() - start;
        // 记录性能指标
        this.recordMetrics({
          endpoint: request.url,
          method: request.method,
          duration,
        });
      })
    );
  }
  
  private recordMetrics(metrics: any) {
    // 发送到监控系统 (Prometheus, DataDog, etc.)
  }
}

🧪 测试规范

单元测试

describe('MCPService', () => {
  let service: MCPService;
  let mockDynamicServerService: jest.Mocked<DynamicServerService>;
  
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        MCPService,
        {
          provide: DynamicServerService,
          useValue: {
            loadOpenAPISpec: jest.fn(),
            getCurrentTools: jest.fn(),
          },
        },
      ],
    }).compile();
    
    service = module.get<MCPService>(MCPService);
    mockDynamicServerService = module.get(DynamicServerService);
  });
  
  it('should handle MCP request correctly', async () => {
    const request: MCPRequest = {
      jsonrpc: '2.0',
      id: '1',
      method: 'tools/list',
    };
    
    mockDynamicServerService.getCurrentTools.mockResolvedValue([]);
    
    const result = await service.handleRequest(request);
    
    expect(result).toBeDefined();
    expect(mockDynamicServerService.getCurrentTools).toHaveBeenCalled();
  });
});

集成测试

describe('AppController (e2e)', () => {
  let app: INestApplication;
  
  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();
    
    app = moduleFixture.createNestApplication();
    await app.init();
  });
  
  it('/api/openapi/configure (POST)', () => {
    return request(app.getHttpServer())
      .post('/api/openapi/configure')
      .send({
        source: {
          type: 'url',
          content: 'https://petstore.swagger.io/v2/swagger.json'
        }
      })
      .expect(200)
      .expect((res) => {
        expect(res.body.success).toBe(true);
        expect(res.body.data.toolsCount).toBeGreaterThan(0);
      });
  });
});

🚀 部署规范

Docker配置

# Dockerfile
FROM node:18-alpine

WORKDIR /app

# 复制依赖文件
COPY package*.json ./
COPY pnpm-lock.yaml ./

# 安装依赖
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile

# 复制源代码
COPY . .

# 构建应用
RUN pnpm run build

# 暴露端口
EXPOSE 3001

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3001/api/health || exit 1

# 启动应用
CMD ["node", "dist/main"]

环境变量配置

# .env.example
# 应用配置
NODE_ENV=development
PORT=3001
MCP_PORT=3322

# CORS配置
CORS_ORIGINS=http://localhost:5173,http://localhost:3000

# 安全配置
API_KEY=your-api-key-here
JWT_SECRET=your-jwt-secret-here

# 日志配置
LOG_LEVEL=info
LOG_FORMAT=json

# 性能配置
REQUEST_TIMEOUT=30000
CACHE_TTL=300

# 监控配置
METRICS_ENABLED=true
HEALTH_CHECK_INTERVAL=30000

PM2配置

{
  "apps": [{
    "name": "mcp-swagger-api",
    "script": "dist/main.js",
    "instances": "max",
    "exec_mode": "cluster",
    "env": {
      "NODE_ENV": "production",
      "PORT": 3001
    },
    "error_file": "logs/err.log",
    "out_file": "logs/out.log",
    "log_file": "logs/combined.log",
    "time": true,
    "max_memory_restart": "512M",
    "node_args": "--max-old-space-size=512"
  }]
}

📚 开发规范

代码风格

// .eslintrc.js
module.exports = {
  extends: [
    '@nestjs',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended'
  ],
  rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
  }
};

Git工作流

# 分支命名规范
feature/add-mcp-protocol      # 新功能
bugfix/fix-session-handling  # Bug修复
hotfix/security-patch        # 紧急修复
refactor/improve-error-handling # 重构

# 提交信息规范
feat: add MCP protocol support
fix: resolve session timeout issue
docs: update API documentation
test: add unit tests for MCPService
refactor: improve error handling

版本发布

// package.json scripts
{
  "scripts": {
    "build": "nest build",
    "start": "node dist/main",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:e2e": "jest --config ./test/jest-e2e.json",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\""
  }
}

这个技术规范为NestJS实施提供了完整的指导涵盖了架构设计、安全规范、监控日志、测试部署等各个方面确保项目能够按照最佳实践进行开发。