UNPKG

spec-workflow-mcp

Version:

MCP server for managing spec workflow (requirements, design, implementation)

166 lines 5.82 kB
import * as yaml from 'js-yaml'; import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import { isObject } from './typeGuards.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Singleton pattern for loading OpenAPI specification export class OpenApiLoader { static instance; spec = null; examples = new Map(); constructor() { } static getInstance() { if (!OpenApiLoader.instance) { OpenApiLoader.instance = new OpenApiLoader(); } return OpenApiLoader.instance; } // Load OpenAPI specification loadSpec() { if (this.spec) { return this.spec; } const specPath = path.join(__dirname, '../../../api/spec-workflow.openapi.yaml'); const specContent = fs.readFileSync(specPath, 'utf8'); this.spec = yaml.load(specContent); // Parse and cache all examples this.cacheExamples(); return this.spec; } // Cache all response examples cacheExamples() { if (!this.spec) return; const schemas = this.spec.components.schemas; for (const [schemaName, schema] of Object.entries(schemas)) { if (!isObject(schema)) continue; // Support standard OpenAPI 3.1.0 examples field if ('examples' in schema && Array.isArray(schema.examples)) { this.examples.set(schemaName, schema.examples); } // Maintain backward compatibility with custom x-examples field else if ('x-examples' in schema && Array.isArray(schema['x-examples'])) { this.examples.set(schemaName, schema['x-examples']); } } } // Get response example getResponseExample(responseType, criteria) { const examples = this.examples.get(responseType); if (!examples || examples.length === 0) { return null; } // If no filter criteria, return the first example if (!criteria) { return examples[0]; } // Filter examples by criteria for (const example of examples) { let matches = true; for (const [key, value] of Object.entries(criteria)) { if (this.getNestedValue(example, key) !== value) { matches = false; break; } } if (matches) { return example; } } // No match found, return the first one return examples[0]; } // Get error response template getErrorResponse(errorType) { if (!this.spec || !this.spec['x-error-responses']) { return null; } const errorResponse = this.spec['x-error-responses'][errorType]; return errorResponse?.displayText || null; } // Get progress calculation rules getProgressRules() { if (!this.spec) return null; const progressSchema = this.spec.components.schemas.Progress; if (isObject(progressSchema) && 'x-progress-rules' in progressSchema) { return progressSchema['x-progress-rules']; } return null; } // Utility function: get nested object value getNestedValue(obj, path) { const keys = path.split('.'); let current = obj; for (const key of keys) { if (isObject(current) && key in current) { current = current[key]; } else { return undefined; } } return current; } // Replace template variables static replaceVariables(template, variables) { let result = template; for (const [key, value] of Object.entries(variables)) { const regex = new RegExp(`\\$\\{${key}\\}`, 'g'); result = result.replace(regex, String(value)); } return result; } // Get shared resource - directly return MCP format getSharedResource(resourceId) { if (!this.spec || !this.spec['x-shared-resources']) { return null; } return this.spec['x-shared-resources'][resourceId] || null; } // Get global configuration getGlobalConfig() { if (!this.spec) return {}; return this.spec['x-global-config'] || {}; } // Get document template getDocumentTemplate(templateType) { if (!this.spec) return null; return this.spec['x-document-templates']?.[templateType] || null; } // Resolve resource list - no conversion needed, use MCP format directly resolveResources(resources) { if (!resources || resources.length === 0) { return undefined; } const resolved = []; for (const resource of resources) { if (isObject(resource) && 'ref' in resource && typeof resource.ref === 'string') { // Get from shared resources - already in MCP format const sharedResource = this.getSharedResource(resource.ref); if (sharedResource) { resolved.push(sharedResource); } } } return resolved.length > 0 ? resolved : undefined; } // Get task guidance template getTaskGuidanceTemplate() { if (!this.spec) return null; return this.spec['x-task-guidance-template'] || null; } // Debug method: get cached examples count getExamplesCount(responseType) { return this.examples.get(responseType)?.length || 0; } } export const openApiLoader = OpenApiLoader.getInstance(); //# sourceMappingURL=openApiLoader.js.map