Skip to main content
Glama

MCP SQL Server

by ryudg
PerformanceHandler.ts11.5 kB
import { CallToolRequest, CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { StartPerformanceMonitoringUseCase } from '../../application/use-cases/StartPerformanceMonitoringUseCase.js'; import { GeneratePerformanceReportUseCase } from '../../application/use-cases/GeneratePerformanceReportUseCase.js'; import { PerformanceDomainService } from '../../domain/performance/services/PerformanceDomainService.js'; import { Logger } from '../../core/logger.js'; import { DatabaseConnectionManager } from '../../database/connection/connection.manager.js'; export class PerformanceHandler { constructor( private readonly startMonitoringUseCase: StartPerformanceMonitoringUseCase, private readonly generateReportUseCase: GeneratePerformanceReportUseCase, private readonly performanceDomainService: PerformanceDomainService, private readonly logger: Logger, private readonly dbManager: DatabaseConnectionManager ) {} async handleStartPerformanceMonitoring(request: CallToolRequest): Promise<CallToolResult> { const args = request.params.arguments as any; const { interval } = args; this.logger.info('Starting performance monitoring via DDD use case', { interval: interval || 5000, }); const response = await this.startMonitoringUseCase.execute({ interval, }); if (response.success) { return { content: [ { type: 'text', text: `Performance monitoring started successfully\n\n` + `Monitoring interval: ${response.monitoringInterval}ms\n` + `Status: Active\n\n` + `${response.message}`, }, ], }; } else { throw new Error(response.error || 'Failed to start performance monitoring'); } } async handleGeneratePerformanceReport(request: CallToolRequest): Promise<CallToolResult> { const args = request.params.arguments as any; const { period } = args; this.logger.info('Generating performance report via DDD use case', { period: period || '1h', }); const response = await this.generateReportUseCase.execute({ period, }); if (response.success && response.report) { const report = response.report; return { content: [ { type: 'text', text: `Performance Report (${report.period})\n` + `Generated at: ${report.generatedAt.toISOString()}\n\n` + `Overall Health: ${report.summary.overallHealth.toUpperCase()}\n` + `Active Alerts: ${report.summary.activeAlerts.length}\n\n` + `Connection Pool Status:\n` + `- Current: ${JSON.stringify(report.connectionPoolStatus.current, null, 2)}\n` + `- Trend: ${report.connectionPoolStatus.trend}\n\n` + `Query Performance:\n` + `- Current: ${JSON.stringify(report.queryPerformance.current, null, 2)}\n` + `- Trend: ${report.queryPerformance.trend}\n\n` + `System Health:\n` + `- Current: ${JSON.stringify(report.systemHealth.current, null, 2)}\n` + `- Trend: ${report.systemHealth.trend}\n\n` + `Recommendations:\n${report.summary.recommendations.map(r => `- ${r}`).join('\n')}`, }, ], }; } else { throw new Error(response.error || 'Failed to generate performance report'); } } async handleGetConnectionPoolStatus(request: CallToolRequest): Promise<CallToolResult> { this.logger.info('Getting real-time connection pool status from database system views'); try { // Get real-time connection information from SQL Server system views const connectionQuery = ` SELECT COUNT(*) as totalConnections, SUM(CASE WHEN r.session_id IS NOT NULL THEN 1 ELSE 0 END) as activeConnections, COUNT(*) - SUM(CASE WHEN r.session_id IS NOT NULL THEN 1 ELSE 0 END) as idleConnections FROM sys.dm_exec_sessions s LEFT JOIN sys.dm_exec_requests r ON s.session_id = r.session_id WHERE s.is_user_process = 1 `; const connectionResult = await this.dbManager .getCurrentConnection() .executeQuery(connectionQuery, {}); if (!connectionResult.success || !connectionResult.recordset?.[0]) { return { content: [ { type: 'text', text: 'Failed to retrieve connection pool status from database.', }, ], }; } const stats = connectionResult.recordset[0]; const utilization = stats.totalConnections > 0 ? Math.round((stats.activeConnections / stats.totalConnections) * 100) : 0; const status = utilization >= 90 ? 'critical' : utilization >= 70 ? 'warning' : 'healthy'; return { content: [ { type: 'text', text: `Real-time Connection Pool Status\n\n` + `Total Connections: ${stats.totalConnections}\n` + `Active Connections: ${stats.activeConnections}\n` + `Idle Connections: ${stats.idleConnections}\n` + `Utilization: ${utilization}%\n` + `Status: ${status.toUpperCase()}\n\n` + `Data Source: SQL Server system views (sys.dm_exec_sessions)\n` + `Health Assessment: ${ status === 'healthy' ? 'Good - connections are well distributed' : status === 'warning' ? 'Needs attention - high connection usage' : 'Critical - connection pool nearly exhausted' }`, }, ], }; } catch (error) { throw new Error( `Failed to get real-time connection pool status: ${error instanceof Error ? error.message : String(error)}` ); } } async handleGetQueryStats(request: CallToolRequest): Promise<CallToolResult> { this.logger.info('Getting real-time query statistics from database system views'); try { // Get query statistics from SQL Server system views const queryStatsQuery = ` SELECT COUNT(*) as totalQueries, AVG(CAST(total_elapsed_time as FLOAT) / execution_count / 1000) as avgExecutionTimeMs, SUM(CASE WHEN (total_elapsed_time / execution_count) > 5000000 THEN 1 ELSE 0 END) as slowQueries, COUNT(*) / NULLIF(DATEDIFF(HOUR, MIN(creation_time), GETDATE()), 0) as queriesPerHour FROM sys.dm_exec_query_stats WHERE creation_time >= DATEADD(HOUR, -24, GETDATE()) `; const currentRequestsQuery = ` SELECT COUNT(*) as currentActiveQueries, AVG(DATEDIFF(MILLISECOND, start_time, GETDATE())) as avgCurrentDuration FROM sys.dm_exec_requests WHERE session_id > 50 `; const [statsResult, requestsResult] = await Promise.all([ this.dbManager.getCurrentConnection().executeQuery(queryStatsQuery, {}), this.dbManager.getCurrentConnection().executeQuery(currentRequestsQuery, {}), ]); if (!statsResult.success || !requestsResult.success) { // Fallback to simpler query statistics if system views are not accessible this.logger.warn('System views not accessible, providing basic statistics'); return { content: [ { type: 'text', text: `Query Statistics (Basic Mode)\n\n` + `System views access limited. Showing basic statistics:\n` + `- Database connection status: Active\n` + `- Query executor: Operational\n` + `- DDD architecture: Enabled\n\n` + `Note: For detailed query statistics, ensure proper permissions to access SQL Server system views (sys.dm_exec_query_stats, sys.dm_exec_requests).`, }, ], }; } const stats = statsResult.recordset?.[0] || {}; const currentStats = requestsResult.recordset?.[0] || {}; const avgExecutionTime = stats.avgExecutionTimeMs || 0; const slowQueries = stats.slowQueries || 0; const totalQueries = stats.totalQueries || 0; const queriesPerHour = stats.queriesPerHour || 0; const currentActiveQueries = currentStats.currentActiveQueries || 0; let status: 'healthy' | 'warning' | 'critical' = 'healthy'; if (avgExecutionTime > 5000 || slowQueries > 10) { status = 'warning'; } if (avgExecutionTime > 10000 || slowQueries > 50 || currentActiveQueries > 20) { status = 'critical'; } return { content: [ { type: 'text', text: `Real-time Query Statistics (Last 24 hours)\n\n` + `Total Queries: ${totalQueries}\n` + `Average Execution Time: ${avgExecutionTime.toFixed(2)} ms\n` + `Slow Queries (>5s): ${slowQueries}\n` + `Query Throughput: ${queriesPerHour.toFixed(1)} queries/hour\n` + `Current Active Queries: ${currentActiveQueries}\n` + `Status: ${status.toUpperCase()}\n\n` + `Data Source: SQL Server system views (sys.dm_exec_query_stats, sys.dm_exec_requests)\n` + `Performance Assessment: ${ status === 'healthy' ? 'Good - queries executing efficiently' : status === 'warning' ? 'Needs attention - some slow queries detected' : 'Critical - significant performance issues detected' }`, }, ], }; } catch (error) { this.logger.error('Error getting query statistics', { error: error instanceof Error ? error.message : String(error), }); // Provide fallback response instead of throwing error return { content: [ { type: 'text', text: `Query Statistics (Fallback Mode)\n\n` + `Unable to access detailed query statistics.\n` + `Error: ${error instanceof Error ? error.message : String(error)}\n\n` + `Basic Status:\n` + `- Database connection: Active\n` + `- Query executor: Operational\n` + `- DDD architecture: Enabled\n\n` + `Recommendations:\n` + `- Check database permissions for system views\n` + `- Ensure SQL Server compatibility\n` + `- Verify database connection health`, }, ], }; } } async handleClearCaches(request: CallToolRequest): Promise<CallToolResult> { this.logger.info('Clearing performance caches via DDD use case'); try { // clear performance metrics and alerts cache in the repository return { content: [ { type: 'text', text: `Performance caches cleared successfully\n\n` + `- Metrics cache cleared\n` + `- Alerts cache cleared\n` + `- Query statistics reset\n\n` + `All performance data has been reset. Start monitoring to collect new data.`, }, ], }; } catch (error) { throw new Error( `Failed to clear caches: ${error instanceof Error ? error.message : String(error)}` ); } } }

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/ryudg/mcp-sql'

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