index.jsā¢7.11 kB
#!/usr/bin/env node
import { Command } from 'commander';
import { MCPServer } from './server.js';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Read package.json for version info
const packageJsonPath = join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
const program = new Command();
program
.name('openapi-mcp')
.description('Pure JavaScript MCP server for OpenAPI specifications')
.version(packageJson.version);
program
.command('serve')
.description('Start the MCP server')
.requiredOption('-s, --spec <path>', 'Path to OpenAPI specification file')
.option('-b, --base-url <url>', 'Base URL for API requests')
.option('-t, --token <token>', 'Bearer token for authentication')
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
.option('--transport <type>', 'Transport type (stdio or http)', 'stdio')
.option('--http-port <port>', 'HTTP server port (when using http transport)', '3000')
.option('--http-host <host>', 'HTTP server host (when using http transport)', 'localhost')
.action(async (options) => {
try {
// Environment variables can override CLI options
const config = {
specPath: options.spec,
baseURL: options.baseUrl || process.env.OPENAPI_BASE_URL || '',
bearerToken: options.token || process.env.OPENAPI_BEARER_TOKEN,
timeout: parseInt(options.timeout),
transport: options.transport,
httpPort: parseInt(options.httpPort),
httpHost: options.httpHost
};
// Validate required options
if (!config.specPath) {
console.error('Error: OpenAPI specification file is required (--spec)');
process.exit(1);
}
// Validate transport type
if (!['stdio', 'http'].includes(config.transport)) {
console.error('Error: Transport must be either "stdio" or "http"');
process.exit(1);
}
// Create and initialize server
const server = new MCPServer(config);
// Handle process termination gracefully
process.on('SIGINT', async () => {
console.error('\n[MCP] Received SIGINT, shutting down gracefully...');
try {
await server.close();
process.exit(0);
} catch (error) {
console.error('[MCP] Error during shutdown:', error.message);
process.exit(1);
}
});
process.on('SIGTERM', async () => {
console.error('\n[MCP] Received SIGTERM, shutting down gracefully...');
try {
await server.close();
process.exit(0);
} catch (error) {
console.error('[MCP] Error during shutdown:', error.message);
process.exit(1);
}
});
// Initialize and start server
await server.initialize();
await server.run();
} catch (error) {
console.error('[MCP] Failed to start server:', error.message);
console.error('[MCP] Stack trace:', error.stack);
process.exit(1);
}
});
program
.command('validate')
.description('Validate OpenAPI specification and show available tools')
.requiredOption('-s, --spec <path>', 'Path to OpenAPI specification file')
.action(async (options) => {
try {
const { OpenAPIProcessor } = await import('./openapi-processor.js');
const processor = new OpenAPIProcessor();
console.log('Validating OpenAPI specification...');
await processor.loadSpec(options.spec);
console.log('ā
OpenAPI specification is valid');
console.log(`š Specification: ${options.spec}`);
console.log(`š·ļø Title: ${processor.spec.info.title}`);
console.log(`š Description: ${processor.spec.info.description || 'None'}`);
console.log(`š Version: ${processor.spec.info.version}`);
const tools = processor.getTools();
console.log(`š§ Available tools: ${tools.length}`);
if (tools.length > 0) {
console.log('\nTools:');
tools.forEach((tool, index) => {
console.log(` ${index + 1}. ${tool.name}`);
console.log(` ${tool.method.toUpperCase()} ${tool.path}`);
console.log(` ${tool.description}`);
const requiredParams = tool.inputSchema.required || [];
if (requiredParams.length > 0) {
console.log(` Required parameters: ${requiredParams.join(', ')}`);
}
console.log('');
});
}
// Show security information
const securitySchemes = processor.getSecuritySchemes();
if (Object.keys(securitySchemes).length > 0) {
console.log('š Security schemes:');
Object.entries(securitySchemes).forEach(([name, scheme]) => {
console.log(` - ${name}: ${scheme.type} (${scheme.scheme || 'N/A'})`);
});
}
const baseUrl = processor.getBaseUrl();
if (baseUrl) {
console.log(`š Default base URL: ${baseUrl}`);
} else {
console.log('š No default base URL specified in spec');
}
} catch (error) {
console.error('ā Validation failed:', error.message);
process.exit(1);
}
});
program
.command('info')
.description('Show information about the MCP server')
.action(() => {
console.log(`${packageJson.name} v${packageJson.version}`);
console.log(packageJson.description);
console.log(`\nSupported OpenAPI versions: 3.x`);
console.log(`Transport protocols: stdio, http`);
console.log(`Authentication: Bearer token`);
console.log(`HTTP methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD`);
console.log('\nEnvironment variables:');
console.log(' OPENAPI_BASE_URL - Default base URL for API requests');
console.log(' OPENAPI_BEARER_TOKEN - Default bearer token for authentication');
console.log('\nExamples:');
console.log(' # Start server with stdio transport (default)');
console.log(` ${packageJson.name} serve -s api.json -b https://api.example.com -t your-token`);
console.log('');
console.log(' # Start server with HTTP transport');
console.log(` ${packageJson.name} serve -s api.json --transport http --http-port 3000`);
console.log('');
console.log(' # Validate specification');
console.log(` ${packageJson.name} validate -s api.json`);
console.log('');
console.log(' # Use environment variables');
console.log(' export OPENAPI_BASE_URL=https://api.example.com');
console.log(' export OPENAPI_BEARER_TOKEN=your-token');
console.log(` ${packageJson.name} serve -s api.json`);
});
// Handle unknown commands
program.on('command:*', (operands) => {
console.error(`Unknown command: ${operands[0]}`);
console.error('See --help for available commands');
process.exit(1);
});
// Show help if no command provided
if (process.argv.length === 2) {
program.help();
}
program.parse(process.argv);