UNPKG

quality-mcp

Version:

An MCP server that analyzes to your codebase, with plugin support for DCD and Simian. 🏍️ "The only Zen you find on the tops of mountains is the Zen you bring up there."

209 lines (173 loc) 5.7 kB
#!/usr/bin/env node /** * Quality MCP Server * Main entry point for the Model Context Protocol server that integrates * Quality code analysis with AI development workflows. */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { PluginManager } from './core/plugin-manager.js'; import { SimianPlugin } from './plugins/simian/index.js'; import { DCDPlugin } from './plugins/dcd/index.js'; import { createLogger } from './utils/logger.js'; import { loadConfig } from './utils/config.js'; const logger = createLogger('main'); /** * Dependency injection for testability * @returns {Object} Dependencies */ function getDeps() { return { Server, QualityMCPServer, StdioServerTransport, PluginManager, SimianPlugin, DCDPlugin, loadConfig, logger, }; } /** * Main server class that orchestrates the MCP server and plugin system */ class QualityMCPServer { constructor(_getDeps = getDeps) { const { Server, PluginManager } = _getDeps(); this.server = new Server( { name: 'quality-mcp-server', version: '0.1.0', }, { capabilities: { tools: {}, resources: {}, prompts: {}, }, } ); this.pluginManager = new PluginManager(this.server); this.config = null; } async initialize(_getDeps = getDeps) { const { loadConfig, logger } = _getDeps(); logger.info('Initializing Quality MCP Server...'); try { // Load configuration this.config = await loadConfig(); logger.info('Configuration loaded successfully'); // Register core plugins await this.registerPlugins(); // Set up error handling this.setupErrorHandling(); logger.info('Server initialized successfully'); } catch (error) { logger.error('Failed to initialize server:', error); throw error; } } async registerPlugins(_getDeps = getDeps) { const { DCDPlugin, SimianPlugin, logger } = _getDeps(); logger.info('Registering plugins...'); try { // Register DCD plugin as the primary analysis tool (open source alternative) const dcdPlugin = new DCDPlugin(this.config.plugins.dcd || this.config.plugins.simian); await this.pluginManager.register('dcd', dcdPlugin); // Register Simian plugin as secondary option (requires commercial license) const simianPlugin = new SimianPlugin(this.config.plugins.simian); await this.pluginManager.register('simian', simianPlugin); logger.info('Plugins registered successfully'); } catch (error) { logger.error('Failed to register plugins:', error); throw error; } } setupErrorHandling(_getDeps = getDeps) { const { logger } = _getDeps(); // Handle server errors this.server.onerror = error => { logger.error('Server error:', error); }; // Handle plugin errors this.pluginManager.on('error', (pluginName, error) => { logger.error(`Plugin error in ${pluginName}:`, error); }); // Handle process errors global.process.on('uncaughtException', error => { logger.error('Uncaught exception:', error); global.process.exit(1); }); global.process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled rejection at:', promise, 'reason:', reason); global.process.exit(1); }); } async start(_getDeps = getDeps) { const { StdioServerTransport, logger } = _getDeps(); logger.info('Starting Quality MCP Server...'); try { // Create stdio transport const transport = new StdioServerTransport(); // Add error handling for transport transport.onerror = error => { logger.error('Transport error:', error); }; // Add error handling for server connection this.server.onerror = error => { logger.error('Server error during connection:', error); }; // Connect server to transport with better error handling await this.server.connect(transport); logger.info('Server started successfully on stdio transport'); logger.info('Ready to receive MCP requests...'); } catch (error) { logger.error('Failed to start server:', error); throw error; } } async shutdown(_getDeps = getDeps) { const { logger } = _getDeps(); logger.info('Shutting down server...'); try { await this.pluginManager.shutdown(); logger.info('Server shutdown complete'); } catch (error) { logger.error('Error during shutdown:', error); } } } /** * Main execution function * @param {Function} _getDeps - Dependency injection */ async function main(_getDeps = getDeps) { const { QualityMCPServer, logger } = _getDeps(); const server = new QualityMCPServer(); try { await server.initialize(); await server.start(); // Handle graceful shutdown global.process.on('SIGINT', async () => { logger.info('Received SIGINT, shutting down...'); await server.shutdown(); global.process.exit(0); }); global.process.on('SIGTERM', async () => { logger.info('Received SIGTERM, shutting down...'); await server.shutdown(); global.process.exit(0); }); } catch (error) { logger.error('Failed to start server:', error); global.process.exit(1); } } // Only run if this file is executed directly if (import.meta.url === `file://${global.process.argv[1]}`) { main().catch(error => { console.error('Fatal error:', error); global.process.exit(1); }); } export { QualityMCPServer, main, getDeps };