mcp-swagger-server/docs/architecture/parser-extraction-implement...

28 KiB

MCP Swagger Parser 抽离实施计划

📋 文档信息

文档标题: MCP Swagger Parser 抽离实施计划
创建日期: 2025-06-17
版本: v1.0
状态: 实施规划
关联文档: Monorepo 重构方案


🎯 实施概览

核心目标

将 OpenAPI 解析逻辑从 mcp-swagger-server 中抽离,形成独立的 mcp-swagger-parser 库,实现:

  • 职责分离和代码解耦
  • 提高代码复用性
  • 改善测试和维护性
  • 为未来扩展奠定基础

预期收益

代码复用性: 0% → 80% (+80%)
测试隔离度: 困难 → 简单 (+200%)
维护复杂度: 高(8/10) → 低(3/10) (-62%)
开发效率: 基准 → +50% 提升

📊 当前状态分析

需要抽离的代码文件

主要文件清单

packages/mcp-swagger-server/src/transform/
├── openapi-to-mcp.ts                    🔄 需要拆分
│   ├── parseOpenApiFromSource()         ➡️ 移至 parser
│   ├── validateOpenApiSpec()            ➡️ 移至 parser  
│   ├── extractApiEndpoints()            ➡️ 移至 parser
│   └── convertToMcpTools()              ⬅️ 保留在 server
│
├── transformOpenApiToMcpTools.ts        🔄 需要重构
│   ├── 解析相关逻辑                      ➡️ 移至 parser
│   └── MCP 转换逻辑                     ⬅️ 保留在 server
│
└── index.ts                             🔄 需要更新导出

代码依赖分析

// 当前 openapi-to-mcp.ts 的依赖
import SwaggerParser from '@apidevtools/swagger-parser';  // ➡️ 移至 parser
import { z } from 'zod';                                  // ➡️ 移至 parser
import axios from 'axios';                                // ➡️ 移至 parser
import * as yaml from 'js-yaml';                          // ➡️ 移至 parser

// MCP 相关依赖 (保留在 server)
import { Tool } from '@modelcontextprotocol/sdk/types.js'; // ⬅️ 保留

功能边界划分

🔍 解析库负责 (mcp-swagger-parser)

interface ParserResponsibilities {
  // 输入处理
  parseFromUrl(url: string): Promise<ParsedApiSpec>;
  parseFromFile(filePath: string): Promise<ParsedApiSpec>;  
  parseFromText(content: string): Promise<ParsedApiSpec>;
  
  // 验证和标准化
  validateSpec(spec: any): Promise<ValidationResult>;
  normalizeSpec(spec: any): Promise<NormalizedApiSpec>;
  
  // 信息提取
  extractEndpoints(spec: ParsedApiSpec): ApiEndpoint[];
  extractSchemas(spec: ParsedApiSpec): SchemaDefinition[];
  extractSecuritySchemes(spec: ParsedApiSpec): SecurityScheme[];
  
  // 错误处理
  handleParseErrors(error: any): ParseError;
}

⚙️ 服务器负责 (mcp-swagger-server)

interface ServerResponsibilities {
  // MCP 转换
  convertToMcpTools(endpoints: ApiEndpoint[]): Tool[];
  applyFilters(endpoints: ApiEndpoint[], filters: FilterConfig): ApiEndpoint[];
  generateMcpConfig(tools: Tool[], config: ConvertConfig): McpConfig;
  
  // 协议处理
  handleMcpProtocol(): void;
  manageTransports(): void;
  
  // 服务器管理
  startServer(): Promise<void>;
  stopServer(): Promise<void>;
}

🏗️ 目标架构设计

新包结构设计

mcp-swagger-parser 包结构

packages/mcp-swagger-parser/
├── src/
│   ├── core/                          # 核心解析器
│   │   ├── parser.ts                  # 主解析器类
│   │   ├── validator.ts               # 规范验证器
│   │   └── normalizer.ts              # 标准化处理器
│   │
│   ├── parsers/                       # 具体解析实现
│   │   ├── url-parser.ts              # URL 输入解析
│   │   ├── file-parser.ts             # 文件输入解析
│   │   ├── text-parser.ts             # 文本输入解析
│   │   └── base-parser.ts             # 解析器基类
│   │
│   ├── extractors/                    # 信息提取器
│   │   ├── endpoint-extractor.ts      # 端点信息提取
│   │   ├── schema-extractor.ts        # 模式定义提取
│   │   ├── security-extractor.ts      # 安全配置提取
│   │   └── metadata-extractor.ts      # 元数据提取
│   │
│   ├── types/                         # TypeScript 类型定义
│   │   ├── input.ts                   # 输入类型
│   │   ├── output.ts                  # 输出类型
│   │   ├── config.ts                  # 配置类型
│   │   └── index.ts                   # 类型导出
│   │
│   ├── utils/                         # 工具函数
│   │   ├── format-detector.ts         # 格式检测
│   │   ├── url-validator.ts           # URL 验证
│   │   ├── error-handler.ts           # 错误处理
│   │   └── logger.ts                  # 日志工具
│   │
│   ├── errors/                        # 错误定义
│   │   ├── parse-error.ts             # 解析错误
│   │   ├── validation-error.ts        # 验证错误
│   │   └── network-error.ts           # 网络错误
│   │
│   └── index.ts                       # 公共 API 导出
│
├── tests/                             # 测试文件
│   ├── unit/                          # 单元测试
│   ├── integration/                   # 集成测试
│   ├── fixtures/                      # 测试数据
│   └── __helpers__/                   # 测试助手
│
├── docs/                              # 文档
│   ├── API.md                         # API 文档
│   ├── examples/                      # 使用示例
│   └── migration.md                   # 迁移指南
│
├── package.json                       # 包配置
├── tsconfig.json                      # TypeScript 配置
├── rollup.config.js                   # 构建配置
└── README.md                          # 包说明

API 设计规范

核心 API 接口

// packages/mcp-swagger-parser/src/index.ts

export class OpenApiParser {
  constructor(options?: ParserOptions) {}

  // 主要解析方法
  async parseFromUrl(url: string, options?: ParseFromUrlOptions): Promise<ParsedApiSpec> {}
  async parseFromFile(filePath: string, options?: ParseFromFileOptions): Promise<ParsedApiSpec> {}
  async parseFromText(content: string, format?: 'json' | 'yaml', options?: ParseFromTextOptions): Promise<ParsedApiSpec> {}

  // 验证方法
  async validate(spec: any): Promise<ValidationResult> {}
  
  // 工具方法
  static detectFormat(content: string): 'json' | 'yaml' | 'unknown' {}
  static isValidUrl(url: string): boolean {}
}

// 便捷方法导出
export const parseOpenApiFromUrl = (url: string, options?: ParseFromUrlOptions) => new OpenApiParser().parseFromUrl(url, options);
export const parseOpenApiFromFile = (filePath: string, options?: ParseFromFileOptions) => new OpenApiParser().parseFromFile(filePath, options);
export const parseOpenApiFromText = (content: string, format?: 'json' | 'yaml', options?: ParseFromTextOptions) => new OpenApiParser().parseFromText(content, format, options);

类型定义系统

// packages/mcp-swagger-parser/src/types/index.ts

export interface ParsedApiSpec {
  // OpenAPI 基础信息
  openapi: string;
  info: ApiInfo;
  servers: ServerInfo[];
  
  // 核心内容
  paths: PathsObject;
  components?: ComponentsObject;
  security?: SecurityRequirement[];
  tags?: TagObject[];
  
  // 解析元数据
  metadata: ParseMetadata;
}

export interface ParseMetadata {
  // 解析信息
  sourceType: 'url' | 'file' | 'text';
  sourceLocation: string;
  parsedAt: Date;
  parsingDuration: number;
  
  // 统计信息
  endpointCount: number;
  schemaCount: number;
  securitySchemeCount: number;
  
  // 版本信息
  openApiVersion: string;
  parserVersion: string;
}

export interface ApiEndpoint {
  // 基础信息
  path: string;
  method: HttpMethod;
  operationId?: string;
  summary?: string;
  description?: string;
  
  // 参数和响应
  parameters: Parameter[];
  requestBody?: RequestBody;
  responses: ResponsesObject;
  
  // 元数据
  tags?: string[];
  deprecated?: boolean;
  security?: SecurityRequirement[];
}

📋 详细实施步骤

阶段 1: 准备工作 (1-2 天)

1.1 创建包结构

# 创建新包目录
mkdir -p packages/mcp-swagger-parser/src/{core,parsers,extractors,types,utils,errors}
mkdir -p packages/mcp-swagger-parser/tests/{unit,integration,fixtures,__helpers__}
mkdir -p packages/mcp-swagger-parser/docs/{examples}

# 创建配置文件
touch packages/mcp-swagger-parser/{package.json,tsconfig.json,rollup.config.js,README.md}

1.2 配置包管理

// packages/mcp-swagger-parser/package.json
{
  "name": "mcp-swagger-parser",
  "version": "0.1.0",
  "description": "OpenAPI/Swagger specification parser for MCP projects",
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.esm.js",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "files": [
    "dist",
    "README.md"
  ],
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "test": "jest",
    "test:watch": "jest --watch",
    "lint": "eslint src/**/*.ts",
    "type-check": "tsc --noEmit"
  },
  "dependencies": {
    "@apidevtools/swagger-parser": "^10.1.0",
    "axios": "^1.6.0",
    "js-yaml": "^4.1.0",
    "zod": "^3.25.28"
  },
  "devDependencies": {
    "@types/js-yaml": "^4.0.5",
    "@types/node": "^20.9.0",
    "jest": "^29.7.0",
    "rollup": "^4.0.0",
    "typescript": "^5.2.0"
  },
  "keywords": [
    "openapi",
    "swagger",
    "parser",
    "mcp",
    "api",
    "specification"
  ]
}

1.3 TypeScript 配置

// packages/mcp-swagger-parser/tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true,
    "composite": true
  },
  "include": ["src/**/*"],
  "exclude": ["dist", "tests", "**/*.test.ts", "**/*.spec.ts"]
}

阶段 2: 核心代码抽离 (3-4 天)

2.1 抽离解析器核心类

// packages/mcp-swagger-parser/src/core/parser.ts

import SwaggerParser from '@apidevtools/swagger-parser';
import { z } from 'zod';
import type { 
  ParsedApiSpec, 
  ParserOptions, 
  ParseFromUrlOptions,
  ParseFromFileOptions,
  ParseFromTextOptions,
  ValidationResult 
} from '../types';

export class OpenApiParser {
  private options: ParserOptions;

  constructor(options: ParserOptions = {}) {
    this.options = {
      validateInput: true,
      resolveRefs: true,
      allowEmpty: false,
      ...options
    };
  }

  async parseFromUrl(url: string, options?: ParseFromUrlOptions): Promise<ParsedApiSpec> {
    const startTime = Date.now();
    
    try {
      // 验证 URL 格式
      if (!this.isValidUrl(url)) {
        throw new Error(`Invalid URL format: ${url}`);
      }

      // 使用 swagger-parser 解析
      const spec = await SwaggerParser.parse(url);
      
      // 标准化和提取信息
      const parsedSpec = await this.processSpec(spec, {
        sourceType: 'url',
        sourceLocation: url,
        parsedAt: new Date(),
        parsingDuration: Date.now() - startTime
      });

      return parsedSpec;
    } catch (error) {
      throw this.handleError(error, 'parseFromUrl', { url });
    }
  }

  // 其他方法实现...
  private async processSpec(spec: any, metadata: Partial<ParseMetadata>): Promise<ParsedApiSpec> {
    // 处理和标准化规范
    // 提取端点信息
    // 生成元数据
  }

  private isValidUrl(url: string): boolean {
    try {
      new URL(url);
      return true;
    } catch {
      return false;
    }
  }

  private handleError(error: any, method: string, context: any): Error {
    // 统一错误处理
  }
}

2.2 抽离端点提取器

// packages/mcp-swagger-parser/src/extractors/endpoint-extractor.ts

import type { ParsedApiSpec, ApiEndpoint, Parameter, ResponsesObject } from '../types';

export class EndpointExtractor {
  static extractEndpoints(spec: ParsedApiSpec): ApiEndpoint[] {
    const endpoints: ApiEndpoint[] = [];

    Object.entries(spec.paths).forEach(([path, pathItem]) => {
      const methods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as const;
      
      methods.forEach(method => {
        const operation = pathItem[method];
        if (!operation) return;

        const endpoint: ApiEndpoint = {
          path,
          method: method.toUpperCase() as HttpMethod,
          operationId: operation.operationId,
          summary: operation.summary,
          description: operation.description,
          parameters: this.extractParameters(operation.parameters || []),
          requestBody: operation.requestBody,
          responses: operation.responses || {},
          tags: operation.tags,
          deprecated: operation.deprecated,
          security: operation.security
        };

        endpoints.push(endpoint);
      });
    });

    return endpoints;
  }

  private static extractParameters(parameters: any[]): Parameter[] {
    // 参数提取逻辑
  }
}

2.3 创建类型定义

// packages/mcp-swagger-parser/src/types/output.ts

export interface ParsedApiSpec {
  openapi: string;
  info: ApiInfo;
  servers: ServerInfo[];
  paths: PathsObject;
  components?: ComponentsObject;
  security?: SecurityRequirement[];
  tags?: TagObject[];
  metadata: ParseMetadata;
}

export interface ApiInfo {
  title: string;
  version: string;
  description?: string;
  termsOfService?: string;
  contact?: ContactObject;
  license?: LicenseObject;
}

export interface ParseMetadata {
  sourceType: 'url' | 'file' | 'text';
  sourceLocation: string;
  parsedAt: Date;
  parsingDuration: number;
  endpointCount: number;
  schemaCount: number;
  securitySchemeCount: number;
  openApiVersion: string;
  parserVersion: string;
}

// 更多类型定义...

阶段 3: 服务器端重构 (2-3 天)

3.1 更新服务器依赖

// packages/mcp-swagger-server/package.json 添加依赖
{
  "dependencies": {
    "mcp-swagger-parser": "workspace:^0.1.0",
    // 移除原有的解析相关依赖
    // "@apidevtools/swagger-parser": "^10.1.0", // 删除
    // "js-yaml": "^4.1.0", // 删除
    // 保留 MCP 相关依赖
    "@modelcontextprotocol/sdk": "^1.12.0"
  }
}

3.2 重构转换逻辑

// packages/mcp-swagger-server/src/converters/mcp-converter.ts

import { OpenApiParser, type ParsedApiSpec, type ApiEndpoint } from 'mcp-swagger-parser';
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import type { ConvertConfig, McpConfig } from '../types';

export class McpConverter {
  private parser: OpenApiParser;

  constructor(parser?: OpenApiParser) {
    this.parser = parser || new OpenApiParser();
  }

  async convertFromSource(source: InputSource, config: ConvertConfig): Promise<McpConfig> {
    // 1. 使用解析库解析 OpenAPI
    let spec: ParsedApiSpec;
    
    switch (source.type) {
      case 'url':
        spec = await this.parser.parseFromUrl(source.content);
        break;
      case 'file':
        spec = await this.parser.parseFromFile(source.content);
        break;
      case 'text':
        spec = await this.parser.parseFromText(source.content);
        break;
      default:
        throw new Error(`Unsupported source type: ${source.type}`);
    }

    // 2. 转换为 MCP 格式
    return this.convertSpecToMcp(spec, config);
  }

  private async convertSpecToMcp(spec: ParsedApiSpec, config: ConvertConfig): Promise<McpConfig> {
    // 提取端点
    const endpoints = EndpointExtractor.extractEndpoints(spec);
    
    // 应用过滤器
    const filteredEndpoints = this.applyFilters(endpoints, config.filters);
    
    // 转换为 MCP 工具
    const tools = this.convertToMcpTools(filteredEndpoints, config);
    
    // 生成 MCP 配置
    return {
      tools,
      metadata: {
        generatedAt: new Date(),
        sourceInfo: spec.info,
        endpointCount: filteredEndpoints.length,
        parserVersion: spec.metadata.parserVersion
      }
    };
  }

  private convertToMcpTools(endpoints: ApiEndpoint[], config: ConvertConfig): Tool[] {
    // MCP 工具转换逻辑 (保留原有逻辑)
  }

  private applyFilters(endpoints: ApiEndpoint[], filters: FilterConfig): ApiEndpoint[] {
    // 过滤逻辑 (保留原有逻辑)
  }
}

3.3 更新服务器主逻辑

// packages/mcp-swagger-server/src/server.ts

import { McpConverter } from './converters/mcp-converter.js';
import { OpenApiParser } from 'mcp-swagger-parser';

export class McpSwaggerServer {
  private converter: McpConverter;

  constructor() {
    const parser = new OpenApiParser({
      validateInput: true,
      resolveRefs: true
    });
    
    this.converter = new McpConverter(parser);
  }

  async handleConvertRequest(source: InputSource, config: ConvertConfig): Promise<McpConfig> {
    try {
      return await this.converter.convertFromSource(source, config);
    } catch (error) {
      // 错误处理
      throw new ConversionError(`Failed to convert API: ${error.message}`, error);
    }
  }
}

阶段 4: 测试开发 (2-3 天)

4.1 解析库单元测试

// packages/mcp-swagger-parser/tests/unit/parser.test.ts

import { OpenApiParser } from '../../src';
import { readFileSync } from 'fs';
import { join } from 'path';

describe('OpenApiParser', () => {
  let parser: OpenApiParser;

  beforeEach(() => {
    parser = new OpenApiParser();
  });

  describe('parseFromUrl', () => {
    it('should parse valid OpenAPI 3.0 spec from URL', async () => {
      const result = await parser.parseFromUrl('https://petstore.swagger.io/v2/swagger.json');
      
      expect(result.openapi).toBeDefined();
      expect(result.info.title).toBe('Swagger Petstore');
      expect(result.metadata.sourceType).toBe('url');
      expect(result.metadata.endpointCount).toBeGreaterThan(0);
    });

    it('should throw error for invalid URL', async () => {
      await expect(parser.parseFromUrl('invalid-url'))
        .rejects.toThrow('Invalid URL format');
    });

    it('should handle network errors gracefully', async () => {
      await expect(parser.parseFromUrl('https://nonexistent.example.com/api.json'))
        .rejects.toThrow(/network|fetch|request/i);
    });
  });

  describe('parseFromFile', () => {
    it('should parse YAML file correctly', async () => {
      const result = await parser.parseFromFile(join(__dirname, '../fixtures/petstore.yaml'));
      
      expect(result.info.title).toBeDefined();
      expect(result.metadata.sourceType).toBe('file');
    });

    it('should parse JSON file correctly', async () => {
      const result = await parser.parseFromFile(join(__dirname, '../fixtures/petstore.json'));
      
      expect(result.info.title).toBeDefined();
      expect(result.metadata.sourceType).toBe('file');
    });
  });

  describe('parseFromText', () => {
    it('should parse JSON text', async () => {
      const jsonText = readFileSync(join(__dirname, '../fixtures/petstore.json'), 'utf8');
      const result = await parser.parseFromText(jsonText, 'json');
      
      expect(result.info.title).toBeDefined();
      expect(result.metadata.sourceType).toBe('text');
    });

    it('should auto-detect format', async () => {
      const jsonText = readFileSync(join(__dirname, '../fixtures/petstore.json'), 'utf8');
      const result = await parser.parseFromText(jsonText);
      
      expect(result.info.title).toBeDefined();
    });
  });
});

4.2 集成测试

// packages/mcp-swagger-server/tests/integration/conversion.test.ts

import { McpSwaggerServer } from '../../src/server';
import type { InputSource, ConvertConfig } from '../../src/types';

describe('MCP Conversion Integration', () => {
  let server: McpSwaggerServer;

  beforeEach(() => {
    server = new McpSwaggerServer();
  });

  it('should convert complete OpenAPI spec to MCP format', async () => {
    const source: InputSource = {
      type: 'url',
      content: 'https://petstore.swagger.io/v2/swagger.json'
    };

    const config: ConvertConfig = {
      filters: {
        methods: ['GET', 'POST'],
        includeDeprecated: false
      },
      transport: 'sse',
      optimization: {
        generateValidation: true,
        includeExamples: true
      }
    };

    const result = await server.handleConvertRequest(source, config);

    expect(result.tools).toBeDefined();
    expect(result.tools.length).toBeGreaterThan(0);
    expect(result.metadata.generatedAt).toBeInstanceOf(Date);
    expect(result.metadata.endpointCount).toBeGreaterThan(0);
  });
});

阶段 5: 文档和发布 (1-2 天)

5.1 API 文档

// packages/mcp-swagger-parser/docs/API.md

# MCP Swagger Parser API Documentation

## Installation

```bash
npm install mcp-swagger-parser

Quick Start

import { OpenApiParser } from 'mcp-swagger-parser';

const parser = new OpenApiParser();

// Parse from URL
const spec = await parser.parseFromUrl('https://api.example.com/swagger.json');

// Parse from file
const spec = await parser.parseFromFile('./api.yaml');

// Parse from text
const spec = await parser.parseFromText(yamlContent, 'yaml');

API Reference

Class: OpenApiParser

Constructor

new OpenApiParser(options?: ParserOptions)

Methods

parseFromUrl(url, options?)

解析远程 URL 的 OpenAPI 规范。

Parameters:

  • url (string): OpenAPI 规范的 URL
  • options (ParseFromUrlOptions, optional): 解析选项

Returns: Promise<ParsedApiSpec>

parseFromFile(filePath, options?)

解析本地文件的 OpenAPI 规范。

Parameters:

  • filePath (string): 文件路径
  • options (ParseFromFileOptions, optional): 解析选项

Returns: Promise<ParsedApiSpec>


#### 5.2 使用示例
```typescript
// packages/mcp-swagger-parser/docs/examples/basic-usage.ts

import { OpenApiParser } from 'mcp-swagger-parser';

async function basicExample() {
  const parser = new OpenApiParser({
    validateInput: true,
    resolveRefs: true
  });

  try {
    // 解析 Petstore API
    const spec = await parser.parseFromUrl('https://petstore.swagger.io/v2/swagger.json');
    
    console.log('API Title:', spec.info.title);
    console.log('API Version:', spec.info.version);
    console.log('Endpoint Count:', spec.metadata.endpointCount);
    
    // 提取端点信息
    const endpoints = EndpointExtractor.extractEndpoints(spec);
    endpoints.forEach(endpoint => {
      console.log(`${endpoint.method} ${endpoint.path}: ${endpoint.summary}`);
    });
    
  } catch (error) {
    console.error('Parse failed:', error.message);
  }
}

basicExample();

实施时间线

总体时间安排

阶段 1: 准备工作        (1-2 天)   ██░░░░░░░░
阶段 2: 核心代码抽离    (3-4 天)   ██████░░░░
阶段 3: 服务器端重构    (2-3 天)   ████░░░░░░
阶段 4: 测试开发        (2-3 天)   ████░░░░░░
阶段 5: 文档和发布      (1-2 天)   ██░░░░░░░░
总计:                   (9-14 天)  ██████████

每日任务分解

Day 1-2: 项目搭建

  • 创建 mcp-swagger-parser 包结构
  • 配置 TypeScript 和构建工具
  • 设置测试环境
  • 更新 workspace 配置

Day 3-4: 类型定义和核心抽离

  • 设计和实现类型定义系统
  • 抽离 OpenApiParser 核心类
  • 实现 URL/文件/文本解析器
  • 创建验证和标准化逻辑

Day 5-6: 信息提取器开发

  • 实现 EndpointExtractor
  • 实现 SchemaExtractor
  • 实现 SecurityExtractor
  • 添加错误处理机制

Day 7-8: 服务器端重构

  • 重构 mcp-swagger-server 依赖
  • 实现 McpConverter
  • 更新服务器主逻辑
  • 测试基本集成

Day 9-10: 测试开发

  • 编写解析库单元测试
  • 编写服务器集成测试
  • 准备测试数据和 fixtures
  • 测试覆盖率优化

Day 11-12: 文档和优化

  • 编写 API 文档
  • 创建使用示例
  • 性能优化和错误处理改进
  • 准备发布材料

Day 13-14: 发布和验证

  • 发布 alpha 版本
  • 端到端测试验证
  • 修复发现的问题
  • 准备正式发布

🎯 成功标准

技术指标

功能完整性

  • 所有现有解析功能正常工作
  • 新解析库独立功能测试通过
  • 服务器集成测试 100% 通过
  • 性能不降低(解析速度保持或提升)

代码质量

  • 测试覆盖率 ≥ 85%
  • TypeScript 严格模式通过
  • ESLint 检查无错误
  • 代码重复率 ≤ 5%

文档完整性

  • API 文档覆盖率 100%
  • 使用示例完整
  • 迁移指南清晰
  • 错误处理文档完善

业务指标

开发体验

  • 新包可以独立开发和测试
  • 构建时间减少 20%
  • 包大小优化 15%
  • 错误信息更加友好

可维护性

  • 代码职责清晰分离
  • 依赖关系简化
  • 新功能开发速度提升
  • Bug 修复时间减少

🚨 风险控制

风险识别和缓解

高风险项目

  1. 破坏性变更风险 🔴

    风险: 重构可能导致现有功能异常
    缓解: 
    - 保持 API 兼容性
    - 全面的回归测试
    - 分阶段发布策略
    
  2. 性能回归风险 🟡

    风险: 新架构可能影响解析性能
    缓解:
    - 性能基准测试
    - 关键路径优化
    - 内存使用监控
    

中风险项目

  1. 依赖冲突风险 🟡

    风险: monorepo 中的依赖版本冲突
    缓解:
    - 统一依赖管理策略
    - 使用 workspace 协议
    - 定期依赖更新
    
  2. 学习成本风险 🟡

    风险: 新架构增加团队学习成本
    缓解:
    - 详细的文档和示例
    - 平滑的迁移路径
    - 培训和知识分享
    

回滚计划

紧急回滚策略

# 如果新版本出现严重问题,快速回滚步骤:

1. 恢复到重构前的代码版本
   git revert <refactor-commit-hash>

2. 暂时移除新的解析库包
   rm -rf packages/mcp-swagger-parser

3. 恢复服务器的原始依赖
   git checkout HEAD~1 packages/mcp-swagger-server/package.json

4. 重新构建和测试
   pnpm install && pnpm build && pnpm test

渐进式回滚

// 通过功能开关支持新旧实现并存
const USE_NEW_PARSER = process.env.USE_NEW_PARSER === 'true';

export async function parseOpenApi(source: InputSource): Promise<ParsedApiSpec> {
  if (USE_NEW_PARSER) {
    // 使用新的解析库
    const parser = new OpenApiParser();
    return parser.parseFromUrl(source.url);
  } else {
    // 使用旧的解析逻辑
    return legacyParseOpenApi(source);
  }
}

📝 总结和下一步

实施总结

这个重构计划将:

  1. 提升架构质量 📈

    • 实现关注点分离
    • 提高代码复用性
    • 改善可测试性
  2. 优化开发体验 🚀

    • 独立包开发
    • 更快的构建速度
    • 清晰的依赖关系
  3. 增强扩展能力 🔮

    • 为未来功能扩展奠定基础
    • 支持多格式解析
    • 便于社区贡献

立即行动项

  1. 今天开始 (Day 1)

    • 创建 packages/mcp-swagger-parser 目录结构
    • 配置基础的 package.jsontsconfig.json
    • 设置基础构建脚本
  2. 本周完成 (Day 1-5)

    • 完成核心代码抽离
    • 实现基础的解析功能
    • 添加必要的测试用例
  3. 下周目标 (Day 6-10)

    • 完成服务器端重构
    • 全面的测试覆盖
    • 基础文档编写

长期愿景

通过这次重构,我们将建立一个:

  • 🎯 模块化的架构 - 每个包都有清晰的职责
  • 🔄 可复用的生态 - 解析库可服务于多个项目
  • 🧪 高质量的代码 - 完善的测试和文档
  • 🚀 快速的开发 - 提升团队开发效率

这不仅是一次技术重构,更是为项目长期发展奠定坚实基础的重要投资。


实施状态跟踪: 本文档将随着实施进展持续更新,确保计划与实际执行保持同步。