UNPKG

mcp-cve-intelligence-server-lite

Version:

Lite Model Context Protocol server for comprehensive CVE intelligence gathering with multi-source exploit discovery, designed for security professionals and cybersecurity researchers

236 lines 12 kB
/* eslint-disable no-console */ import { Command } from 'commander'; import { createContextLogger } from '../utils/logger.js'; import { VERSION } from '../version.js'; import { setEnvironmentFromCLI, getAppConfiguration, setEnvironmentVariable, setEnvironmentIfNotSet, configManager, } from '../config/index.js'; const logger = createContextLogger('CLIService'); export class CLIService { program; version; constructor() { // Use auto-generated version from version.ts this.version = VERSION; logger.debug('CLIService initialized', { version: this.version }); this.program = new Command(); this.setupCommands(); } setupCommands() { this.program .name('mcp-cve-intelligence-server-lite') .description('MCP CVE Intelligence Server Lite - Search and analyze CVE vulnerabilities with in-memory storage') .version(this.version) .option('-t, --transport <type>', 'transport type (stdio|http)') .option('-p, --port <number>', 'HTTP port (when using http transport)', '3001') .option('-H, --host <host>', 'HTTP host (when using http transport)', '0.0.0.0') .option('-l, --log-level <level>', 'log level (error|warn|info|debug)', 'info') .option('-c, --sources-config <path>', 'path to custom cve-sources.json file') .helpOption('-?, --help', 'display help for command'); // Quick start command - no options, just uses optimal defaults this.program .command('quick-start [port]') .description('Start server with optimal defaults for quick testing (optional port as argument)') .action(async (port) => { const quickStartOptions = { port: port || '3001', }; await this.handleQuickStart(quickStartOptions); }); // Config command this.program .command('config') .description('Show current configuration and environment') .option('--json', 'show only the complete JSON configuration') .option('--full', 'show only the complete JSON configuration (alias for --json)') .action((options) => { this.handleConfigCommand(options); }); // Default action for main command this.program.action(async (options) => { await this.handleDefaultCommand(options); }); } async handleQuickStart(options) { logger.debug('Quick start command initiated', { options }); // Use default port if not provided const port = options.port || '3001'; // Validate port this.validatePort(port, 'quick-start'); // Set environment variables for quick start using centralized config setEnvironmentVariable('MCP_TRANSPORT_TYPE', 'http'); // Force HTTP for quick-start setEnvironmentVariable('MCP_HTTP_PORT', port); setEnvironmentIfNotSet('MCP_HTTP_HOST', '0.0.0.0'); setEnvironmentIfNotSet('MCP_HTTP_CORS', 'true'); // Reload configuration to pick up the new environment variables const config = configManager.reload(); logger.debug('Quick start configuration applied', { port: config.transport.http.port, host: config.transport.http.host, cors: config.transport.http.enableCors, }); this.displayMessage('info', ''); this.displayMessage('info', 'Starting MCP CVE Intelligence Server Lite (Quick Start Mode)'); this.displayMessage('info', 'Transport: HTTP'); this.displayMessage('info', `Port: ${config.transport.http.port}`); this.displayMessage('info', ''); // Import and start server dynamically to avoid circular dependencies const { startServer } = await import('./server-launcher.js'); await startServer(config.transport.type); } handleConfigCommand(options) { logger.debug('Config command initiated', { options }); const config = getAppConfiguration(); logger.debug('Current configuration', { config }); // If --json or --full flag is used, show only the JSON configuration if (options?.json || options?.full) { try { // Create a sanitized version of the config for display (only mask sensitive API keys) const sanitizedConfig = JSON.parse(JSON.stringify(config)); // Only mask API keys in security section - sources config is not sensitive if (sanitizedConfig.security?.apiKeys) { for (const [key, value] of Object.entries(sanitizedConfig.security.apiKeys)) { if (value && typeof value === 'string') { sanitizedConfig.security.apiKeys[key] = '***configured***'; } } } this.displayMessage('info', JSON.stringify(sanitizedConfig, null, 2)); } catch (error) { this.displayMessage('info', `Error serializing configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); } process.exit(0); return; } // Default behavior: show formatted configuration summary this.displayMessage('info', 'MCP CVE Intelligence Server Lite Configuration:'); this.displayMessage('info', ''); // Display CVE Sources Configuration this.displayMessage('info', 'CVE Sources Configuration:'); try { if (config.sources && Object.keys(config.sources).length > 0) { for (const [sourceKey, sourceConfig] of Object.entries(config.sources)) { const status = sourceConfig.enabled ? '✓ enabled' : '✗ disabled'; const apiKeyStatus = sourceConfig.apiKeyRequired ? (process.env[sourceConfig.apiKeyEnvVar || ''] ? 'with API key' : 'missing API key') : 'no API key required'; // Display basic info this.displayMessage('info', ` ${sourceKey}: ${sourceConfig.name}`); this.displayMessage('info', ` Status: ${status}`); this.displayMessage('info', ` API Key: ${apiKeyStatus}`); // Display additional details if available if ('priority' in sourceConfig) { this.displayMessage('info', ` Priority: ${sourceConfig.priority}`); } if ('features' in sourceConfig && Array.isArray(sourceConfig.features)) { this.displayMessage('info', ` Features: ${sourceConfig.features.join(', ')}`); } this.displayMessage('info', ''); } } else { this.displayMessage('info', ' No sources configured'); this.displayMessage('info', ''); } } catch (error) { this.displayMessage('info', ` Error loading sources: ${error instanceof Error ? error.message : 'Unknown error'}`); this.displayMessage('info', ''); } this.displayMessage('info', 'Complete Configuration (JSON):'); this.displayMessage('info', ''); try { // Create a sanitized version of the config for display (only mask sensitive API keys) const sanitizedConfig = JSON.parse(JSON.stringify(config)); // Only mask API keys in security section - sources config is not sensitive if (sanitizedConfig.security?.apiKeys) { for (const [key, value] of Object.entries(sanitizedConfig.security.apiKeys)) { if (value && typeof value === 'string') { sanitizedConfig.security.apiKeys[key] = '***configured***'; } } } this.displayMessage('info', JSON.stringify(sanitizedConfig, null, 2)); } catch (error) { this.displayMessage('info', `Error serializing configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); } this.displayMessage('info', ''); this.displayMessage('info', 'Available Commands:'); this.displayMessage('info', ' npx mcp-cve-intelligence-server-lite # Start with default settings'); this.displayMessage('info', ' npx mcp-cve-intelligence-server-lite quick-start # HTTP mode for testing'); this.displayMessage('info', ' npx mcp-cve-intelligence-server-lite --help # Show all options'); this.displayMessage('info', ''); this.displayMessage('info', 'MCP Client Configuration Examples:'); this.displayMessage('info', ' See README.md for VS Code and Claude Desktop setup'); // Exit the process after displaying configuration process.exit(0); } async handleDefaultCommand(options) { logger.debug('Default command initiated', { options }); // Validate CLI options this.validateTransport(options.transport); this.validatePort(options.port, 'main command'); this.validateLogLevel(options.logLevel); // Set environment variables from CLI options - ONLY if not already set by environment this.setEnvironmentFromOptions(options); // Reload configuration to pick up CLI options (especially custom sources config) const config = configManager.reload(); logger.info('Starting server with configuration', { transport: config.transport.type, logLevel: config.logging.level, }); // Import and start server dynamically to avoid circular dependencies const { startServer } = await import('./server-launcher.js'); await startServer(config.transport.type); } setEnvironmentFromOptions(options) { logger.debug('Setting environment from CLI options', { options }); // Use centralized configuration management setEnvironmentFromCLI(options); logger.debug('Environment variables set from CLI options'); } displayMessage(level, ...args) { if (level === 'debug') { console.log('DEBUG -', ...args); } else { console.log(...args); } } validatePort(port, commandName) { if (port) { const portNum = parseInt(port, 10); if (isNaN(portNum) || portNum < 1 || portNum > 65535) { logger.error(`Invalid port number for ${commandName}: ${port}`); this.displayMessage('info', `Error: Invalid port number '${port}'. Port must be between 1 and 65535.`); process.exit(1); } if (portNum < 1024) { logger.warn(`Port ${portNum} requires elevated privileges`); this.displayMessage('info', `Warning: Port ${portNum} may require elevated privileges (sudo/root).`); } } } validateTransport(transport) { if (transport && !['stdio', 'http'].includes(transport)) { logger.error(`Invalid transport type: ${transport}`); this.displayMessage('info', `Error: Invalid transport type '${transport}'. Must be 'stdio' or 'http'.`); process.exit(1); } } validateLogLevel(logLevel) { if (logLevel && !['error', 'warn', 'info', 'debug'].includes(logLevel)) { logger.error(`Invalid log level: ${logLevel}`); this.displayMessage('info', `Error: Invalid log level '${logLevel}'. Must be 'error', 'warn', 'info', or 'debug'.`); process.exit(1); } } parse(argv) { logger.debug('Parsing CLI arguments', { argv }); this.program.parse(argv); } getProgram() { return this.program; } } //# sourceMappingURL=cli-service.js.map