UNPKG

mcp-swagger-parser

Version:

Enterprise-grade OpenAPI/Swagger specification parser for Model Context Protocol (MCP) projects

154 lines 5.39 kB
"use strict"; /** * OpenAPI specification validator */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Validator = void 0; const swagger_parser_1 = __importDefault(require("@apidevtools/swagger-parser")); class Validator { constructor(config) { this.config = config; } /** * Validate OpenAPI specification */ async validate(spec) { const errors = []; const warnings = []; // Basic structure validation this.validateBasicStructure(spec, errors, warnings); // Schema validation using swagger-parser if enabled if (this.config.validateSchema) { try { await swagger_parser_1.default.validate(spec); } catch (error) { if (error instanceof Error) { errors.push({ path: 'root', message: error.message, code: 'SCHEMA_VALIDATION_ERROR', severity: 'error' }); } } } // Custom validators if (this.config.customValidators) { for (const validator of this.config.customValidators) { try { const result = validator.validate(spec); errors.push(...result.errors); warnings.push(...result.warnings); } catch (error) { warnings.push({ path: 'root', message: `Custom validator '${validator.name}' failed: ${error instanceof Error ? error.message : String(error)}`, code: 'CUSTOM_VALIDATOR_ERROR', severity: 'warning' }); } } } return { valid: errors.length === 0, isValid: errors.length === 0, errors, warnings }; } /** * Validate basic OpenAPI specification structure */ validateBasicStructure(spec, errors, warnings) { // Check required fields if (!spec.openapi) { errors.push({ path: 'openapi', message: 'Missing required field: openapi', code: 'MISSING_REQUIRED_FIELD', severity: 'error' }); } else if (!spec.openapi.startsWith('3.')) { warnings.push({ path: 'openapi', message: 'Only OpenAPI 3.x is fully supported', code: 'UNSUPPORTED_VERSION', severity: 'warning' }); } if (!spec.info) { errors.push({ path: 'info', message: 'Missing required field: info', code: 'MISSING_REQUIRED_FIELD', severity: 'error' }); } else { if (!spec.info.title) { errors.push({ path: 'info.title', message: 'Missing required field: info.title', code: 'MISSING_REQUIRED_FIELD', severity: 'error' }); } if (!spec.info.version) { errors.push({ path: 'info.version', message: 'Missing required field: info.version', code: 'MISSING_REQUIRED_FIELD', severity: 'error' }); } } if (!spec.paths) { errors.push({ path: 'paths', message: 'Missing required field: paths', code: 'MISSING_REQUIRED_FIELD', severity: 'error' }); } else if (Object.keys(spec.paths).length === 0 && !this.config.allowEmptyPaths) { warnings.push({ path: 'paths', message: 'No paths defined in specification', code: 'EMPTY_PATHS', severity: 'warning' }); } // Validate paths if (spec.paths) { for (const [path, pathItem] of Object.entries(spec.paths)) { if (!path.startsWith('/')) { errors.push({ path: `paths.${path}`, message: 'Path must start with forward slash', code: 'INVALID_PATH_FORMAT', severity: 'error' }); } // Check for operations const hasOperations = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'] .some(method => pathItem[method]); if (!hasOperations && !pathItem.$ref) { warnings.push({ path: `paths.${path}`, message: 'Path item has no operations defined', code: 'NO_OPERATIONS', severity: 'warning' }); } } } } } exports.Validator = Validator; //# sourceMappingURL=validator.js.map