Skip to main content
Glama
optimized-main.ts12.7 kB
#!/usr/bin/env node /** * Optimized Grafana MCP Server with advanced performance features */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { getConfig } from './config.js'; import { ToolRegistry } from './tool-registry.js'; import { ServiceContainer, StringInterner, AsyncOperationManager } from './service-pool.js'; import { OptimizedGrafanaHttpClient } from './optimized-http-client.js'; import { performanceMonitor } from './performance-monitor.js'; // Import service classes import { DashboardService } from './services/dashboard.js'; import { DatasourceService } from './services/datasource.js'; import { PrometheusService } from './services/prometheus.js'; import { LokiService } from './services/loki.js'; import { AlertingService } from './services/alerting.js'; import { AdminService } from './services/admin.js'; import { NavigationService } from './services/navigation.js'; // Import tool registration functions import { registerDashboardTools } from './tools/dashboard.js'; import { registerDatasourceTools } from './tools/datasource.js'; import { registerPrometheusTools } from './tools/prometheus.js'; import { registerLokiTools } from './tools/loki.js'; import { registerAlertingTools } from './tools/alerting.js'; import { registerAdminTools } from './tools/admin.js'; import { registerNavigationTools } from './tools/navigation.js'; /** * Optimized Grafana MCP Server class */ class OptimizedGrafanaMCPServer { private server: Server; private registry: ToolRegistry; private serviceContainer: ServiceContainer; private asyncManager: AsyncOperationManager; private stringInterner: StringInterner; private isShuttingDown = false; constructor() { this.server = new Server( { name: 'grafana-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, }, ); this.registry = new ToolRegistry(); this.serviceContainer = new ServiceContainer(); this.asyncManager = new AsyncOperationManager(15, 8); // Higher concurrency this.stringInterner = StringInterner.getInstance(); this.setupOptimizedServices(); this.setupGracefulShutdown(); this.setupPerformanceMonitoring(); } /** * Setup services with dependency injection and pooling */ private setupOptimizedServices(): void { const startTime = Date.now(); const config = getConfig(); // Register HTTP client with singleton pattern this.serviceContainer.registerSingleton('httpClient', () => { return new OptimizedGrafanaHttpClient(config); }); // Register services as singletons for better resource management this.serviceContainer.registerSingleton('dashboardService', () => { return new DashboardService(this.serviceContainer.get('httpClient')); }); this.serviceContainer.registerSingleton('dataSourceService', () => { return new DatasourceService(this.serviceContainer.get('httpClient')); }); this.serviceContainer.registerSingleton('prometheusService', () => { return new PrometheusService(this.serviceContainer.get('httpClient')); }); this.serviceContainer.registerSingleton('lokiService', () => { return new LokiService(this.serviceContainer.get('httpClient')); }); this.serviceContainer.registerSingleton('alertingService', () => { return new AlertingService(this.serviceContainer.get('httpClient')); }); this.serviceContainer.registerSingleton('adminService', () => { return new AdminService(this.serviceContainer.get('httpClient')); }); this.serviceContainer.registerSingleton('navigationService', () => { return new NavigationService(this.serviceContainer.get('httpClient')); }); console.log('✓ Services registered with optimized container'); console.log(`⚡ Service setup completed in ${Date.now() - startTime}ms`); } /** * Register all MCP tools with optimization */ private async registerTools(): Promise<void> { const startTime = Date.now(); const config = getConfig(); const disabledTools = new Set(config.GRAFANA_DISABLE_TOOLS); const toolRegistrations = [ { name: 'dashboards', fn: registerDashboardTools, service: 'dashboardService' }, { name: 'datasources', fn: registerDatasourceTools, service: 'dataSourceService' }, { name: 'prometheus', fn: registerPrometheusTools, service: 'prometheusService' }, { name: 'loki', fn: registerLokiTools, service: 'lokiService' }, { name: 'alerting', fn: registerAlertingTools, service: 'alertingService' }, { name: 'admin', fn: registerAdminTools, service: 'adminService' }, { name: 'navigation', fn: registerNavigationTools, service: 'navigationService' }, ]; // Register tools in parallel using async manager const registrationPromises = toolRegistrations .filter(({ name }) => !disabledTools.has(name)) .map(({ name, fn, service }) => this.asyncManager.enqueue(`tool-registration-${name}`, async () => { const serviceInstance = this.serviceContainer.get(service) as any; fn(this.registry, serviceInstance); return name; }), ); const registeredTools = await Promise.all(registrationPromises); console.log(`✓ Registered ${registeredTools.length} tool categories:`, registeredTools.join(', ')); // Log registry statistics const stats = this.registry.getStats(); console.log(`✓ Total tools registered: ${stats.totalTools}`); console.log(`✓ Categories: ${Object.entries(stats.categories).map(([cat, count]) => `${cat}(${count})`).join(', ')}`); console.log(`⚡ Tool registration completed in ${Date.now() - startTime}ms`); } /** * Setup MCP protocol handlers */ private setupProtocolHandlers(): void { // Optimized list tools handler with caching this.server.setRequestHandler(ListToolsRequestSchema, async () => { return this.asyncManager.enqueue('list-tools', async () => { const tools = this.registry.getTools(); // Intern strings to reduce memory usage const optimizedTools = tools.map(tool => ({ name: this.stringInterner.intern(tool.name), description: this.stringInterner.intern(tool.description), inputSchema: tool.inputSchema, })); return { tools: optimizedTools }; }); }); // Optimized call tool handler with performance monitoring this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const startTime = Date.now(); const toolName = request.params.name; return this.asyncManager.enqueue(`tool-${toolName}`, async () => { try { if (!this.registry.hasTool(toolName)) { throw new Error(`Unknown tool: ${toolName}`); } const handler = this.registry.getHandler(toolName); if (!handler) { throw new Error(`No handler found for tool: ${toolName}`); } performanceMonitor.startTimer(`tool-${toolName}`); const result = await handler(request as any); const duration = performanceMonitor.endTimer(`tool-${toolName}`); // Log slow operations if (duration > 5000) { console.warn(`Slow tool execution: ${toolName} took ${duration.toFixed(2)}ms`); } return result; } catch (error) { const duration = Date.now() - startTime; console.error(`Tool ${toolName} failed after ${duration}ms:`, error); return { content: [ { type: 'text', text: `Error executing ${toolName}: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }); }); } /** * Setup performance monitoring */ private setupPerformanceMonitoring(): void { performanceMonitor.on('performance-alert', (alerts: string[]) => { console.warn('🚨 Performance Alert:', alerts); }); // Periodic optimization every 5 minutes setInterval(() => { if (!this.isShuttingDown) { this.optimize(); } }, 5 * 60 * 1000); // Performance report every 15 minutes setInterval(() => { if (!this.isShuttingDown) { this.logPerformanceReport(); } }, 15 * 60 * 1000); } /** * Optimize system resources */ private optimize(): void { try { // Optimize service container this.serviceContainer.optimize(); // Optimize HTTP client const httpClient = this.serviceContainer.get('httpClient') as OptimizedGrafanaHttpClient; httpClient.optimize(); // Clear string interner periodically if (Math.random() < 0.1) { // 10% chance this.stringInterner.clear(); } // Force garbage collection if available if (global.gc && Math.random() < 0.2) { // 20% chance global.gc(); } console.log('🔧 System optimization completed'); } catch (error) { console.warn('Optimization error:', error); } } /** * Log performance metrics */ private logPerformanceReport(): void { try { const report = performanceMonitor.getPerformanceReport(); const httpClient = this.serviceContainer.get('httpClient') as OptimizedGrafanaHttpClient; const httpMetrics = httpClient.getPerformanceMetrics(); console.log('📊 Performance Report:'); console.log(' Memory:', `${report.summary.memoryUsageMB.toFixed(1)}MB`); console.log(' Avg Response Time:', `${report.summary.avgResponseTime.toFixed(2)}ms`); console.log(' Cache Hit Rate:', `${(report.summary.cacheHitRate * 100).toFixed(1)}%`); console.log(' Error Rate:', `${(report.summary.errorRate * 100).toFixed(1)}%`); console.log(' Active Requests:', httpMetrics.activeRequests); console.log(' Queue Length:', httpMetrics.queueLength); if (report.recommendations.length > 0) { console.log('💡 Recommendations:'); report.recommendations.forEach(rec => console.log(` - ${rec}`)); } } catch (error) { console.warn('Performance report error:', error); } } /** * Setup graceful shutdown */ private setupGracefulShutdown(): void { const shutdown = async (signal: string) => { console.log(`\n🛑 Received ${signal}, shutting down gracefully...`); this.isShuttingDown = true; try { // Stop accepting new requests await this.server.close(); // Cleanup resources this.serviceContainer.cleanup(); this.asyncManager.clear(); const httpClient = this.serviceContainer.get('httpClient') as OptimizedGrafanaHttpClient; httpClient.cleanup(); performanceMonitor.cleanup(); console.log('✓ Graceful shutdown completed'); process.exit(0); } catch (error) { console.error('Shutdown error:', error); process.exit(1); } }; process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); process.on('SIGQUIT', () => shutdown('SIGQUIT')); process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection at:', promise, 'reason:', reason); process.exit(1); }); } /** * Start the server */ async start(): Promise<void> { try { console.log('🚀 Starting Optimized Grafana MCP Server...'); // Register tools await this.registerTools(); // Setup protocol handlers this.setupProtocolHandlers(); // Connect to stdio transport const transport = new StdioServerTransport(); await this.server.connect(transport); console.log('✅ Optimized Grafana MCP Server started successfully'); console.log('📊 Performance monitoring enabled'); // Log initial metrics this.logPerformanceReport(); } catch (error) { console.error('❌ Failed to start server:', error); process.exit(1); } } } // Start the optimized server const server = new OptimizedGrafanaMCPServer(); server.start().catch((error) => { console.error('Fatal error:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quanticsoul4772/grafana-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server