UNPKG

openapi-spec-master

Version:

🚀 Professional OpenAPI specification analyzer and MCP server. The ultimate toolkit for API developers with VS Code extension integration.

917 lines (879 loc) 38.6 kB
#!/usr/bin/env node import express from 'express'; import cors from 'cors'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { OpenAPIParser } from '../utils/openapi-parser.js'; import { generateAnalytics } from '../utils/analytics.js'; class OpenAPIExplorerHTTPServer { app; server; parser; currentSpec = null; currentEndpoints = []; port; constructor(port = 3001) { this.port = port; this.app = express(); this.parser = new OpenAPIParser(); this.server = new Server({ name: 'openapi-explorer-http', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.setupMiddleware(); this.setupRoutes(); this.setupToolHandlers(); } setupMiddleware() { this.app.use(cors({ origin: '*', methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], })); this.app.use(express.json({ limit: '10mb' })); this.app.use(express.urlencoded({ extended: true })); // Request logging this.app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); }); } setupRoutes() { // Health check this.app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), hasSpec: !!this.currentSpec, endpoints: this.currentEndpoints.length }); }); // MCP protocol endpoints this.app.post('/mcp/tools/list', async (req, res) => { try { const result = await this.handleListTools(); res.json(result); } catch (error) { this.handleError(res, error); } }); this.app.post('/mcp/tools/call', async (req, res) => { try { const { name, arguments: args } = req.body; const result = await this.handleCallTool(name, args); res.json(result); } catch (error) { this.handleError(res, error); } }); // Streaming endpoint for real-time updates this.app.get('/mcp/stream', (req, res) => { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', }); // Send initial connection message res.write(`data: ${JSON.stringify({ type: 'connection', message: 'Connected to OpenAPI Explorer MCP Server', timestamp: new Date().toISOString() })}\n\n`); // Keep connection alive const keepAlive = setInterval(() => { res.write(`data: ${JSON.stringify({ type: 'ping', timestamp: new Date().toISOString() })}\n\n`); }, 30000); req.on('close', () => { clearInterval(keepAlive); }); }); // API information endpoint this.app.get('/api/info', (req, res) => { if (!this.currentSpec) { return res.status(404).json({ error: 'No OpenAPI specification loaded' }); } res.json({ title: this.currentSpec.info.title, version: this.currentSpec.info.version, description: this.currentSpec.info.description, endpoints: this.currentEndpoints.length, analytics: generateAnalytics(this.currentEndpoints) }); }); // Direct endpoint search this.app.get('/api/endpoints', (req, res) => { if (!this.currentEndpoints.length) { return res.status(404).json({ error: 'No endpoints available' }); } const { query, method, tag, limit = 50 } = req.query; let filtered = [...this.currentEndpoints]; if (query) { const searchTerm = query.toLowerCase(); filtered = filtered.filter(ep => ep.path.toLowerCase().includes(searchTerm) || ep.summary?.toLowerCase().includes(searchTerm) || ep.description?.toLowerCase().includes(searchTerm)); } if (method) { filtered = filtered.filter(ep => ep.method.toLowerCase() === method.toLowerCase()); } if (tag) { filtered = filtered.filter(ep => ep.tags.includes(tag)); } const limitNum = parseInt(limit); const results = filtered.slice(0, limitNum); res.json({ total: filtered.length, limit: limitNum, results: results.map(ep => ({ method: ep.method, path: ep.path, summary: ep.summary, tags: ep.tags, complexity: ep.complexity, deprecated: ep.deprecated })) }); }); // WebSocket-like endpoint for real-time tool execution this.app.post('/mcp/execute', async (req, res) => { const { tool, args, stream = false } = req.body; if (stream) { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', }); try { // Send start event res.write(`data: ${JSON.stringify({ type: 'start', tool, timestamp: new Date().toISOString() })}\n\n`); const result = await this.handleCallTool(tool, args); // Send result event res.write(`data: ${JSON.stringify({ type: 'result', data: result, timestamp: new Date().toISOString() })}\n\n`); // Send completion event res.write(`data: ${JSON.stringify({ type: 'complete', timestamp: new Date().toISOString() })}\n\n`); res.end(); } catch (error) { res.write(`data: ${JSON.stringify({ type: 'error', error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString() })}\n\n`); res.end(); } } else { try { const result = await this.handleCallTool(tool, args); res.json(result); } catch (error) { this.handleError(res, error); } } }); // Documentation endpoint this.app.get('/docs', (req, res) => { res.json({ name: 'OpenAPI Explorer MCP Server', version: '1.0.0', description: 'HTTP transport for OpenAPI specification analysis and exploration', endpoints: { 'GET /health': 'Server health check', 'POST /mcp/tools/list': 'List available MCP tools', 'POST /mcp/tools/call': 'Execute an MCP tool', 'GET /mcp/stream': 'Server-sent events stream', 'POST /mcp/execute': 'Execute tool with optional streaming', 'GET /api/info': 'Get loaded API information', 'GET /api/endpoints': 'Search endpoints directly', 'GET /docs': 'This documentation' }, tools: [ 'load_openapi_spec', 'get_api_overview', 'search_endpoints', 'get_endpoint_details', 'generate_code_examples', 'get_api_analytics', 'validate_api_design', 'export_documentation' ] }); }); } setupToolHandlers() { // Use the same tool handlers as the stdio server this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'load_openapi_spec', description: 'Load and parse an OpenAPI specification from text, URL, or file content', inputSchema: { type: 'object', properties: { source: { type: 'string', description: 'The source of the OpenAPI spec (text content, URL, or file content)', }, sourceType: { type: 'string', enum: ['text', 'url'], description: 'Type of source: text (JSON/YAML content) or url', default: 'text', }, }, required: ['source'], }, }, { name: 'get_api_overview', description: 'Get a comprehensive overview of the loaded API including basic info, statistics, and analytics', inputSchema: { type: 'object', properties: {}, }, }, { name: 'search_endpoints', description: 'Search and filter API endpoints with advanced criteria', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for endpoint paths, summaries, or descriptions', }, methods: { type: 'array', items: { type: 'string' }, description: 'Filter by HTTP methods (GET, POST, PUT, DELETE, etc.)', }, tags: { type: 'array', items: { type: 'string' }, description: 'Filter by endpoint tags', }, complexity: { type: 'array', items: { type: 'string', enum: ['low', 'medium', 'high'] }, description: 'Filter by endpoint complexity level', }, deprecated: { type: 'boolean', description: 'Filter by deprecation status', }, hasParameters: { type: 'boolean', description: 'Filter endpoints that have parameters', }, hasRequestBody: { type: 'boolean', description: 'Filter endpoints that require a request body', }, }, }, }, { name: 'get_endpoint_details', description: 'Get detailed information about a specific endpoint', inputSchema: { type: 'object', properties: { method: { type: 'string', description: 'HTTP method (GET, POST, etc.)', }, path: { type: 'string', description: 'Endpoint path', }, }, required: ['method', 'path'], }, }, { name: 'generate_code_examples', description: 'Generate code examples for specific endpoints in various languages', inputSchema: { type: 'object', properties: { method: { type: 'string', description: 'HTTP method', }, path: { type: 'string', description: 'Endpoint path', }, language: { type: 'string', enum: ['curl', 'javascript', 'python', 'typescript'], description: 'Programming language for the example', default: 'curl', }, }, required: ['method', 'path'], }, }, { name: 'get_api_analytics', description: 'Get comprehensive analytics and insights about the API', inputSchema: { type: 'object', properties: { includeDistributions: { type: 'boolean', description: 'Include method, tag, and complexity distributions', default: true, }, }, }, }, { name: 'validate_api_design', description: 'Analyze the API design and provide recommendations for improvements', inputSchema: { type: 'object', properties: { focus: { type: 'string', enum: ['security', 'performance', 'design', 'documentation', 'all'], description: 'Focus area for validation', default: 'all', }, }, }, }, { name: 'export_documentation', description: 'Export API documentation in various formats', inputSchema: { type: 'object', properties: { format: { type: 'string', enum: ['markdown', 'json', 'summary'], description: 'Export format', default: 'markdown', }, includeExamples: { type: 'boolean', description: 'Include code examples in export', default: true, }, includeAnalytics: { type: 'boolean', description: 'Include analytics in export', default: false, }, }, }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; return await this.handleCallTool(name, args); }); } async handleListTools() { // Return the tools list directly since we know what tools we have return { tools: [ { name: 'load_openapi_spec', description: 'Load and parse an OpenAPI specification from text, URL, or file content', inputSchema: { type: 'object', properties: { source: { type: 'string', description: 'The source of the OpenAPI spec (text content, URL, or file content)', }, sourceType: { type: 'string', enum: ['text', 'url'], description: 'Type of source: text (JSON/YAML content) or url', default: 'text', }, }, required: ['source'], }, }, // Add other tools here... ], }; } async handleCallTool(name, args) { try { switch (name) { case 'load_openapi_spec': return await this.loadOpenAPISpec(args); case 'get_api_overview': return await this.getAPIOverview(); case 'search_endpoints': return await this.searchEndpoints(args); case 'get_endpoint_details': return await this.getEndpointDetails(args); case 'generate_code_examples': return await this.generateCodeExamples(args); case 'get_api_analytics': return await this.getAPIAnalytics(args); case 'validate_api_design': return await this.validateAPIDesign(args); case 'export_documentation': return await this.exportDocumentation(args); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } handleError(res, error) { console.error('HTTP Server Error:', error); if (error instanceof McpError) { res.status(400).json({ error: { code: error.code, message: error.message } }); } else { res.status(500).json({ error: { code: 'INTERNAL_ERROR', message: error instanceof Error ? error.message : 'Unknown error occurred' } }); } } // Tool implementation methods (same as stdio server) async loadOpenAPISpec(args) { const { source, sourceType = 'text' } = args; try { let spec; if (sourceType === 'url') { spec = await this.parser.parseFromUrl(source); } else { spec = await this.parser.parseFromText(source); } this.currentSpec = spec; this.currentEndpoints = this.parser.extractEndpoints(); const analytics = generateAnalytics(this.currentEndpoints); return { content: [ { type: 'text', text: `✅ Successfully loaded OpenAPI specification! **API Information:** - Title: ${spec.info.title} - Version: ${spec.info.version} - Description: ${spec.info.description || 'No description provided'} - OpenAPI Version: ${spec.openapi} **Statistics:** - Total Endpoints: ${this.currentEndpoints.length} - HTTP Methods: ${Object.keys(analytics.methodDistribution).join(', ')} - Tags: ${Object.keys(analytics.tagDistribution).length} - Deprecated Endpoints: ${analytics.deprecatedCount} The API specification has been loaded and is ready for exploration. You can now use other tools to search endpoints, get analytics, generate code examples, and more!`, }, ], }; } catch (error) { throw new McpError(ErrorCode.InvalidRequest, `Failed to load OpenAPI spec: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async getAPIOverview() { if (!this.currentSpec || !this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first using load_openapi_spec.'); } const analytics = generateAnalytics(this.currentEndpoints); const tags = this.parser.getAllTags(); return { content: [ { type: 'text', text: `# ${this.currentSpec.info.title} - API Overview ## Basic Information - **Version:** ${this.currentSpec.info.version} - **OpenAPI Version:** ${this.currentSpec.openapi} - **Description:** ${this.currentSpec.info.description || 'No description provided'} ## Statistics - **Total Endpoints:** ${analytics.totalEndpoints} - **Deprecated Endpoints:** ${analytics.deprecatedCount} (${((analytics.deprecatedCount / analytics.totalEndpoints) * 100).toFixed(1)}%) - **Average Parameters per Endpoint:** ${analytics.averageParametersPerEndpoint.toFixed(1)} - **Security Schemes:** ${analytics.securitySchemes.length} ## HTTP Methods Distribution ${Object.entries(analytics.methodDistribution) .map(([method, count]) => `- **${method}:** ${count} endpoints`) .join('\n')} ## Tags (${tags.length}) ${tags.length > 0 ? tags.map(tag => `- ${tag}`).join('\n') : 'No tags defined'} ## Complexity Distribution ${Object.entries(analytics.complexityDistribution) .map(([complexity, count]) => `- **${complexity.charAt(0).toUpperCase() + complexity.slice(1)}:** ${count} endpoints`) .join('\n')}`, }, ], }; } async searchEndpoints(args) { if (!this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first.'); } const { query, methods, tags, complexity, deprecated, hasParameters, hasRequestBody, } = args; let filteredEndpoints = [...this.currentEndpoints]; // Apply filters (same logic as stdio server) if (query) { const searchTerm = query.toLowerCase(); filteredEndpoints = filteredEndpoints.filter(endpoint => endpoint.path.toLowerCase().includes(searchTerm) || endpoint.summary?.toLowerCase().includes(searchTerm) || endpoint.description?.toLowerCase().includes(searchTerm) || endpoint.tags.some(tag => tag.toLowerCase().includes(searchTerm))); } if (methods && methods.length > 0) { filteredEndpoints = filteredEndpoints.filter(endpoint => methods.includes(endpoint.method)); } if (tags && tags.length > 0) { filteredEndpoints = filteredEndpoints.filter(endpoint => endpoint.tags.some(tag => tags.includes(tag))); } if (complexity && complexity.length > 0) { filteredEndpoints = filteredEndpoints.filter(endpoint => endpoint.complexity && complexity.includes(endpoint.complexity)); } if (typeof deprecated === 'boolean') { filteredEndpoints = filteredEndpoints.filter(endpoint => endpoint.deprecated === deprecated); } if (typeof hasParameters === 'boolean') { filteredEndpoints = filteredEndpoints.filter(endpoint => (endpoint.parameters.length > 0) === hasParameters); } if (typeof hasRequestBody === 'boolean') { filteredEndpoints = filteredEndpoints.filter(endpoint => !!endpoint.requestBody === hasRequestBody); } const results = filteredEndpoints.slice(0, 20); return { content: [ { type: 'text', text: `# Search Results Found **${filteredEndpoints.length}** endpoints matching your criteria${filteredEndpoints.length > 20 ? ' (showing first 20)' : ''}: ${results.map(endpoint => ` ## ${endpoint.method} ${endpoint.path} - **Summary:** ${endpoint.summary || 'No summary'} - **Tags:** ${endpoint.tags.join(', ') || 'None'} - **Complexity:** ${endpoint.complexity || 'Unknown'} - **Parameters:** ${endpoint.parameters.length} - **Deprecated:** ${endpoint.deprecated ? 'Yes' : 'No'} - **Has Request Body:** ${endpoint.requestBody ? 'Yes' : 'No'} ${endpoint.description ? `- **Description:** ${endpoint.description}` : ''} `).join('\n')}`, }, ], }; } async getEndpointDetails(args) { if (!this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first.'); } const { method, path } = args; const endpoint = this.currentEndpoints.find(ep => ep.method.toLowerCase() === method.toLowerCase() && ep.path === path); if (!endpoint) { throw new McpError(ErrorCode.InvalidRequest, `Endpoint ${method} ${path} not found.`); } return { content: [ { type: 'text', text: `# ${endpoint.method} ${endpoint.path} ## Overview - **Summary:** ${endpoint.summary || 'No summary provided'} - **Description:** ${endpoint.description || 'No description provided'} - **Tags:** ${endpoint.tags.join(', ') || 'None'} - **Deprecated:** ${endpoint.deprecated ? '⚠️ Yes' : '✅ No'} - **Complexity:** ${endpoint.complexity || 'Unknown'} ## Parameters (${endpoint.parameters.length}) ${endpoint.parameters.length > 0 ? endpoint.parameters.map(param => ` ### ${param.name} (${param.in}) - **Type:** ${(param.schema && 'type' in param.schema) ? param.schema.type : 'Unknown'} - **Required:** ${param.required ? 'Yes' : 'No'} - **Description:** ${param.description || 'No description'} `).join('\n') : 'No parameters'} ## Request Body ${endpoint.requestBody ? 'This endpoint accepts a request body' : 'No request body required'} ## Responses ${Object.entries(endpoint.responses).map(([code, response]) => ` ### ${code} ${response.description} `).join('\n')}`, }, ], }; } async generateCodeExamples(args) { if (!this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first.'); } const { method, path, language = 'curl' } = args; const endpoint = this.currentEndpoints.find(ep => ep.method.toLowerCase() === method.toLowerCase() && ep.path === path); if (!endpoint) { throw new McpError(ErrorCode.InvalidRequest, `Endpoint ${method} ${path} not found.`); } let example = ''; switch (language) { case 'curl': example = this.generateCurlExample(endpoint); break; case 'javascript': example = this.generateJavaScriptExample(endpoint); break; case 'python': example = this.generatePythonExample(endpoint); break; case 'typescript': example = this.generateTypeScriptExample(endpoint); break; default: throw new McpError(ErrorCode.InvalidRequest, `Unsupported language: ${language}`); } return { content: [ { type: 'text', text: `# Code Example: ${endpoint.method} ${endpoint.path} ## ${language.charAt(0).toUpperCase() + language.slice(1)} Example \`\`\`${language === 'typescript' ? 'typescript' : language} ${example} \`\`\` ## Endpoint Details - **Summary:** ${endpoint.summary || 'No summary'} - **Parameters:** ${endpoint.parameters.length} - **Request Body:** ${endpoint.requestBody ? 'Required' : 'Not required'}`, }, ], }; } async getAPIAnalytics(args) { if (!this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first.'); } const { includeDistributions = true } = args; const analytics = generateAnalytics(this.currentEndpoints); let result = `# API Analytics ## Overview Statistics - **Total Endpoints:** ${analytics.totalEndpoints} - **Deprecated Endpoints:** ${analytics.deprecatedCount} (${((analytics.deprecatedCount / analytics.totalEndpoints) * 100).toFixed(1)}%) - **Average Parameters per Endpoint:** ${analytics.averageParametersPerEndpoint.toFixed(1)} - **Security Schemes:** ${analytics.securitySchemes.length} ## Security Analysis ${analytics.securitySchemes.length > 0 ? `**Security Schemes Used:** ${analytics.securitySchemes.join(', ')}` : '⚠️ **No security schemes detected** - Consider adding authentication'}`; if (includeDistributions) { result += ` ## Method Distribution ${Object.entries(analytics.methodDistribution) .sort(([, a], [, b]) => b - a) .map(([method, count]) => `- **${method}:** ${count} endpoints (${((count / analytics.totalEndpoints) * 100).toFixed(1)}%)`) .join('\n')} ## Complexity Distribution ${Object.entries(analytics.complexityDistribution) .map(([complexity, count]) => `- **${complexity.charAt(0).toUpperCase() + complexity.slice(1)}:** ${count} endpoints (${((count / analytics.totalEndpoints) * 100).toFixed(1)}%)`) .join('\n')}`; } return { content: [ { type: 'text', text: result, }, ], }; } async validateAPIDesign(args) { if (!this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first.'); } const { focus = 'all' } = args; const analytics = generateAnalytics(this.currentEndpoints); const recommendations = []; // Security validation if (focus === 'security' || focus === 'all') { if (analytics.securitySchemes.length === 0) { recommendations.push('🔒 **Security:** No security schemes detected. Consider adding authentication to protect your API.'); } } // Documentation validation if (focus === 'documentation' || focus === 'all') { const undocumentedEndpoints = this.currentEndpoints.filter(ep => !ep.summary && !ep.description); if (undocumentedEndpoints.length > 0) { recommendations.push(`📝 **Documentation:** ${undocumentedEndpoints.length} endpoints lack summaries or descriptions.`); } } if (recommendations.length === 0) { recommendations.push('✅ **Great job!** No major issues detected in your API design.'); } return { content: [ { type: 'text', text: `# API Design Validation Report ## Analysis Focus: ${focus.charAt(0).toUpperCase() + focus.slice(1)} ## Recommendations ${recommendations.map(rec => rec).join('\n\n')} ## Summary Statistics - **Total Endpoints:** ${analytics.totalEndpoints} - **Security Coverage:** ${((this.currentEndpoints.filter(ep => ep.security && ep.security.length > 0).length / analytics.totalEndpoints) * 100).toFixed(1)}% - **Documentation Coverage:** ${((this.currentEndpoints.filter(ep => ep.summary || ep.description).length / analytics.totalEndpoints) * 100).toFixed(1)}%`, }, ], }; } async exportDocumentation(args) { if (!this.currentSpec || !this.currentEndpoints.length) { throw new McpError(ErrorCode.InvalidRequest, 'No OpenAPI specification loaded. Please load a spec first.'); } const { format = 'markdown', includeExamples = true, includeAnalytics = false } = args; if (format === 'summary') { return { content: [ { type: 'text', text: `# ${this.currentSpec.info.title} - API Summary **Version:** ${this.currentSpec.info.version} **Total Endpoints:** ${this.currentEndpoints.length} ## All Endpoints ${this.currentEndpoints.map(ep => `- ${ep.method} ${ep.path}${ep.summary ? ` - ${ep.summary}` : ''}`).join('\n')}`, }, ], }; } // Default markdown format let markdown = `# ${this.currentSpec.info.title} **Version:** ${this.currentSpec.info.version} **OpenAPI Version:** ${this.currentSpec.openapi} ${this.currentSpec.info.description ? `## Description\n${this.currentSpec.info.description}\n` : ''} ## Endpoints (${this.currentEndpoints.length}) `; for (const endpoint of this.currentEndpoints.slice(0, 10)) { // Limit for HTTP response markdown += `### ${endpoint.method} ${endpoint.path}\n\n`; if (endpoint.summary) { markdown += `**Summary:** ${endpoint.summary}\n\n`; } if (includeExamples) { markdown += `**Example:**\n\`\`\`bash\n${this.generateCurlExample(endpoint)}\n\`\`\`\n\n`; } markdown += '---\n\n'; } return { content: [ { type: 'text', text: markdown, }, ], }; } // Code generation methods (same as stdio server) generateCurlExample(endpoint) { const method = endpoint.method.toLowerCase(); let curl = `curl -X ${endpoint.method} "${endpoint.path}"`; if (endpoint.parameters?.some(p => p.in === 'header')) { curl += ` \\\n -H "Content-Type: application/json"`; } if (endpoint.requestBody && (method === 'post' || method === 'put' || method === 'patch')) { curl += ` \\\n -d '{"example": "data"}'`; } return curl; } generateJavaScriptExample(endpoint) { const hasBody = endpoint.requestBody && ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase()); return `const response = await fetch('${endpoint.path}', { method: '${endpoint.method}', headers: { 'Content-Type': 'application/json', },${hasBody ? ` body: JSON.stringify({ // Add your request data here }),` : ''} }); const data = await response.json(); console.log(data);`; } generatePythonExample(endpoint) { const hasBody = endpoint.requestBody && ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase()); return `import requests url = "${endpoint.path}" headers = {"Content-Type": "application/json"} ${hasBody ? ` data = { # Add your request data here } response = requests.${endpoint.method.toLowerCase()}(url, headers=headers, json=data)` : ` response = requests.${endpoint.method.toLowerCase()}(url, headers=headers)`} print(response.json())`; } generateTypeScriptExample(endpoint) { const hasBody = endpoint.requestBody && ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase()); return `interface ApiResponse { // Define your response type here } ${hasBody ? `interface RequestData { // Define your request data type here } ` : ''}const response = await fetch('${endpoint.path}', { method: '${endpoint.method}', headers: { 'Content-Type': 'application/json', },${hasBody ? ` body: JSON.stringify({ // Add your request data here } as RequestData),` : ''} }); const data: ApiResponse = await response.json(); console.log(data);`; } start() { this.app.listen(this.port, () => { console.log(`🚀 OpenAPI Explorer MCP HTTP Server running on port ${this.port}`); console.log(`📖 Documentation: http://localhost:${this.port}/docs`); console.log(`🔍 Health check: http://localhost:${this.port}/health`); console.log(`📡 Streaming: http://localhost:${this.port}/mcp/stream`); }); } } // Start the server const port = process.env.PORT ? parseInt(process.env.PORT) : 3001; const server = new OpenAPIExplorerHTTPServer(port); server.start();