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."

277 lines (231 loc) 7.59 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 { spawn } from 'node:child_process'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; 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'); const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** * 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) { process.env.MCP_SERVER = 'true'; 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); } } function printUsage() { console.log('quality-mcp'); console.log(''); console.log('Usage:'); console.log(' quality-mcp Start MCP server (stdio)'); console.log(' quality-mcp setup-dcd Install and verify DCD'); console.log(' quality-mcp setup Setup Simian (requires license)'); console.log(' quality-mcp --help Show this help'); } function runScript(scriptName, args = []) { const scriptPath = resolve(PACKAGE_ROOT, 'scripts', scriptName); return new Promise((resolvePromise, rejectPromise) => { const child = spawn('bash', [scriptPath, ...args], { stdio: 'inherit' }); child.on('error', error => { rejectPromise(error); }); child.on('exit', code => { if (code === 0) { resolvePromise(); return; } rejectPromise(new Error(`Script ${scriptName} exited with code ${code}`)); }); }); } async function runCliCommand(command, args) { switch (command) { case 'setup-dcd': await runScript('setup-dcd.sh', args); return true; case 'setup': await runScript('setup-simian.sh', args); return true; case '-h': case '--help': printUsage(); return true; default: return false; } } // Only run if this file is executed directly if (import.meta.url === `file://${global.process.argv[1]}`) { const [, , command, ...args] = global.process.argv; runCliCommand(command, args) .then(handled => { if (!handled) { // Set environment variable to indicate MCP server mode process.env.MCP_SERVER = 'true'; return main().catch(error => { console.error('Fatal error:', error); global.process.exit(1); }); } }) .catch(error => { console.error('Fatal error:', error); global.process.exit(1); }); } export { QualityMCPServer, main, getDeps };