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
JavaScript
/**
* 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 };