Skip to main content
Glama

MCP Server Trello

health-endpoints.ts17.4 kB
import { z } from 'zod'; import { TrelloHealthMonitor, SystemHealthReport, HealthStatus } from './health-monitor.js'; import { TrelloClient } from '../trello-client.js'; /** * Health endpoint result structure for MCP tools */ interface HealthEndpointResult { [x: string]: unknown; content: Array<{ type: 'text'; text: string }>; isError?: boolean; } /** * Repair operation result */ interface RepairResult { attempted: boolean; success: boolean; actions_taken: string[]; message: string; } /** * THE MAGNIFICENT HEALTH ENDPOINTS COLLECTION! 🏥 * * This class provides all the cardiovascular monitoring APIs that keep * our Trello MCP organism in peak condition. It's like having a team of * world-class physicians monitoring your API 24/7! * * Available endpoints: * - /health - Quick health check * - /health/detailed - Comprehensive diagnostic report * - /health/metadata - Metadata consistency verification * - /health/performance - Performance metrics analysis * - /admin/repair - Automated repair capabilities (when available) */ export class TrelloHealthEndpoints { private healthMonitor: TrelloHealthMonitor; private trelloClient: TrelloClient; constructor(trelloClient: TrelloClient) { this.trelloClient = trelloClient; this.healthMonitor = new TrelloHealthMonitor(trelloClient); } /** * GET /health * Quick health status check - the digital pulse check! * Perfect for load balancers and monitoring systems. */ async getBasicHealth(): Promise<HealthEndpointResult> { try { const healthReport = await this.healthMonitor.getSystemHealth(false); const quickReport = { status: healthReport.overall_status, timestamp: healthReport.timestamp, uptime_ms: healthReport.uptime_ms, checks_passed: healthReport.checks.filter(c => c.status === HealthStatus.HEALTHY).length, total_checks: healthReport.checks.length, response_time_ms: Math.round(healthReport.performance_metrics.avg_response_time_ms), success_rate: `${healthReport.performance_metrics.success_rate_percent}%`, }; return { content: [ { type: 'text', text: JSON.stringify(quickReport, null, 2), }, ], isError: healthReport.overall_status === HealthStatus.CRITICAL, }; } catch (error) { return this.createErrorResponse('Health check failed', error); } } /** * GET /health/detailed * Comprehensive health diagnostic - the full medical examination! * Includes all subsystem checks, performance metrics, and recommendations. */ async getDetailedHealth(): Promise<HealthEndpointResult> { try { const healthReport = await this.healthMonitor.getSystemHealth(true); return { content: [ { type: 'text', text: JSON.stringify(healthReport, null, 2), }, ], isError: healthReport.overall_status === HealthStatus.CRITICAL, }; } catch (error) { return this.createErrorResponse('Detailed health check failed', error); } } /** * GET /health/metadata * Metadata consistency verification - the data integrity scanner! * Checks for consistency between boards, lists, cards, and checklists. */ async getMetadataHealth(): Promise<HealthEndpointResult> { try { const startTime = Date.now(); const metadataReport = await this.performMetadataConsistencyCheck(); const duration = Date.now() - startTime; const result = { status: metadataReport.consistent ? HealthStatus.HEALTHY : HealthStatus.DEGRADED, timestamp: new Date().toISOString(), duration_ms: duration, metadata_consistency: metadataReport, recommendations: this.generateMetadataRecommendations(metadataReport), }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], isError: !metadataReport.consistent, }; } catch (error) { return this.createErrorResponse('Metadata health check failed', error); } } /** * GET /health/performance * Performance metrics analysis - the cardiovascular stress test! * Deep dive into response times, throughput, and system efficiency. */ async getPerformanceHealth(): Promise<HealthEndpointResult> { try { const healthReport = await this.healthMonitor.getSystemHealth(false); const performanceAnalysis = this.analyzePerformanceMetrics(healthReport); return { content: [ { type: 'text', text: JSON.stringify(performanceAnalysis, null, 2), }, ], isError: performanceAnalysis.status === HealthStatus.CRITICAL, }; } catch (error) { return this.createErrorResponse('Performance health check failed', error); } } /** * POST /admin/repair * Automated system repair - the digital emergency room! * Attempts to automatically fix common issues when possible. */ async performRepair(): Promise<HealthEndpointResult> { try { const healthReport = await this.healthMonitor.getSystemHealth(true); if (!healthReport.repair_available) { return { content: [ { type: 'text', text: JSON.stringify( { repair_attempted: false, reason: 'No repairable issues detected or system in critical state', status: healthReport.overall_status, recommendations: healthReport.recommendations, }, null, 2 ), }, ], }; } const repairResult = await this.attemptSystemRepair(healthReport); return { content: [ { type: 'text', text: JSON.stringify(repairResult, null, 2), }, ], isError: !repairResult.success, }; } catch (error) { return this.createErrorResponse('System repair failed', error); } } /** * Perform comprehensive metadata consistency check */ private async performMetadataConsistencyCheck() { const results = { consistent: true, issues: [] as string[], statistics: {} as Record<string, any>, last_check: new Date().toISOString(), }; try { // Check if we have an active board const boardId = this.trelloClient.activeBoardId; if (!boardId) { results.consistent = false; results.issues.push('No active board configured'); return results; } // Get board information const board = await this.trelloClient.getBoardById(boardId); if (board.closed) { results.consistent = false; results.issues.push('Active board is closed/archived'); } // Get lists and check consistency const lists = await this.trelloClient.getLists(); results.statistics.total_lists = lists.length; results.statistics.open_lists = lists.filter(l => !l.closed).length; results.statistics.closed_lists = lists.filter(l => l.closed).length; // Check for empty board if (lists.length === 0) { results.issues.push('Board has no lists'); } // Get user cards for comparison const myCards = await this.trelloClient.getMyCards(); results.statistics.total_user_cards = myCards.length; results.statistics.open_user_cards = myCards.filter(c => !c.closed).length; // Check workspace consistency const workspaceId = this.trelloClient.activeWorkspaceId; if (workspaceId) { try { const workspace = await this.trelloClient.getWorkspaceById(workspaceId); results.statistics.active_workspace = workspace.displayName; } catch (error) { results.consistent = false; results.issues.push('Active workspace is inaccessible'); } } // Check checklist accessibility (non-critical) try { const acceptanceCriteria = await this.trelloClient.getAcceptanceCriteria(); results.statistics.acceptance_criteria_items = acceptanceCriteria.length; } catch (error) { // This is not critical for consistency results.statistics.checklist_note = 'Acceptance Criteria checklist not found (non-critical)'; } } catch (error) { results.consistent = false; results.issues.push( `Metadata check error: ${error instanceof Error ? error.message : 'Unknown error'}` ); } return results; } /** * Generate metadata-specific recommendations */ private generateMetadataRecommendations(metadataReport: any): string[] { const recommendations: string[] = []; if (metadataReport.issues.some((issue: string) => issue.includes('No active board'))) { recommendations.push('Use set_active_board tool to configure an active board'); } if (metadataReport.issues.some((issue: string) => issue.includes('closed/archived'))) { recommendations.push('Set a different active board that is not closed/archived'); } if (metadataReport.issues.some((issue: string) => issue.includes('no lists'))) { recommendations.push('Create lists in your board using add_list_to_board tool'); } if (metadataReport.statistics.total_user_cards === 0) { recommendations.push( 'Consider assigning yourself to some cards for better workflow tracking' ); } if (recommendations.length === 0) { recommendations.push('Metadata consistency is excellent - no action required'); } return recommendations; } /** * Analyze performance metrics in detail */ private analyzePerformanceMetrics(healthReport: SystemHealthReport) { const metrics = healthReport.performance_metrics; const performanceGrade = this.calculatePerformanceGrade(metrics); return { status: this.getPerformanceStatus(performanceGrade), timestamp: healthReport.timestamp, performance_grade: performanceGrade, metrics: { ...metrics, uptime_hours: Math.round((healthReport.uptime_ms / (1000 * 60 * 60)) * 100) / 100, health_check_duration_ms: healthReport.checks.reduce((sum, c) => sum + c.duration_ms, 0), }, analysis: { response_time_rating: this.rateResponseTime(metrics.avg_response_time_ms), success_rate_rating: this.rateSuccessRate(metrics.success_rate_percent), throughput_rating: this.rateThroughput(metrics.requests_per_minute), rate_limit_health: this.rateRateLimitUtilization(metrics.rate_limit_utilization_percent), }, recommendations: this.generatePerformanceRecommendations(metrics), }; } /** * Calculate overall performance grade */ private calculatePerformanceGrade(metrics: any): string { let score = 0; // Response time scoring (40% weight) if (metrics.avg_response_time_ms < 200) score += 40; else if (metrics.avg_response_time_ms < 500) score += 35; else if (metrics.avg_response_time_ms < 1000) score += 25; else if (metrics.avg_response_time_ms < 2000) score += 15; else score += 5; // Success rate scoring (35% weight) if (metrics.success_rate_percent >= 99) score += 35; else if (metrics.success_rate_percent >= 95) score += 30; else if (metrics.success_rate_percent >= 90) score += 20; else if (metrics.success_rate_percent >= 80) score += 10; else score += 5; // Rate limit utilization scoring (25% weight) if (metrics.rate_limit_utilization_percent < 50) score += 25; else if (metrics.rate_limit_utilization_percent < 70) score += 20; else if (metrics.rate_limit_utilization_percent < 85) score += 15; else if (metrics.rate_limit_utilization_percent < 95) score += 10; else score += 5; if (score >= 90) return 'A+'; if (score >= 80) return 'A'; if (score >= 70) return 'B'; if (score >= 60) return 'C'; if (score >= 50) return 'D'; return 'F'; } /** * Get performance status based on grade */ private getPerformanceStatus(grade: string): HealthStatus { if (['A+', 'A', 'B'].includes(grade)) return HealthStatus.HEALTHY; if (['C', 'D'].includes(grade)) return HealthStatus.DEGRADED; return HealthStatus.CRITICAL; } /** * Rate individual performance aspects */ private rateResponseTime(avgMs: number): string { if (avgMs < 200) return 'excellent'; if (avgMs < 500) return 'good'; if (avgMs < 1000) return 'fair'; if (avgMs < 2000) return 'slow'; return 'very_slow'; } private rateSuccessRate(percent: number): string { if (percent >= 99) return 'excellent'; if (percent >= 95) return 'good'; if (percent >= 90) return 'fair'; if (percent >= 80) return 'poor'; return 'critical'; } private rateThroughput(requestsPerMin: number): string { if (requestsPerMin > 30) return 'high'; if (requestsPerMin > 15) return 'moderate'; if (requestsPerMin > 5) return 'low'; return 'very_low'; } private rateRateLimitUtilization(percent: number): string { if (percent < 50) return 'optimal'; if (percent < 70) return 'moderate'; if (percent < 85) return 'high'; if (percent < 95) return 'near_limit'; return 'critical'; } /** * Generate performance-specific recommendations */ private generatePerformanceRecommendations(metrics: any): string[] { const recommendations: string[] = []; if (metrics.avg_response_time_ms > 1000) { recommendations.push( 'High response times detected - check network connectivity and Trello API status' ); } if (metrics.success_rate_percent < 95) { recommendations.push( 'Low success rate - investigate error patterns and implement retry logic' ); } if (metrics.rate_limit_utilization_percent > 80) { recommendations.push( 'High rate limit utilization - consider implementing request caching or batching' ); } if (metrics.requests_per_minute < 1) { recommendations.push('Very low API usage - ensure the MCP server is being actively used'); } if (recommendations.length === 0) { recommendations.push('Performance is excellent - maintain current usage patterns'); } return recommendations; } /** * Attempt to repair common system issues */ private async attemptSystemRepair(healthReport: SystemHealthReport): Promise<RepairResult> { const result: RepairResult = { attempted: true, success: false, actions_taken: [], message: '', }; try { // Check for repairable issues const boardCheck = healthReport.checks.find(c => c.name === 'board_access'); if ( boardCheck?.status === HealthStatus.DEGRADED && boardCheck.message.includes('No active board configured') ) { // Attempt to set first available board as active const boards = await this.trelloClient.listBoards(); const openBoards = boards.filter(b => !b.closed); if (openBoards.length > 0) { await this.trelloClient.setActiveBoard(openBoards[0].id); result.actions_taken.push(`Set active board to "${openBoards[0].name}"`); } } // Add more repair logic here as needed result.success = result.actions_taken.length > 0; result.message = result.success ? 'System repair completed successfully' : 'No repairable issues found'; } catch (error) { result.success = false; result.message = `Repair failed: ${error instanceof Error ? error.message : 'Unknown error'}`; } return result; } /** * Create standardized error response */ private createErrorResponse(message: string, error: unknown): HealthEndpointResult { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return { content: [ { type: 'text', text: JSON.stringify( { error: message, details: errorMessage, timestamp: new Date().toISOString(), status: HealthStatus.CRITICAL, }, null, 2 ), }, ], isError: true, }; } } /** * Zod schemas for health endpoint validation */ export const HealthEndpointSchemas = { basicHealth: { title: 'Get Basic Health', description: 'Get quick system health status for monitoring and load balancing', inputSchema: {}, }, detailedHealth: { title: 'Get Detailed Health', description: 'Get comprehensive system health diagnostic with all subsystem checks', inputSchema: {}, }, metadataHealth: { title: 'Get Metadata Health', description: 'Verify metadata consistency between boards, lists, cards, and checklists', inputSchema: {}, }, performanceHealth: { title: 'Get Performance Health', description: 'Get detailed performance metrics and analysis', inputSchema: {}, }, repair: { title: 'Perform System Repair', description: 'Attempt to automatically repair common system issues', inputSchema: {}, }, };

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/delorenj/mcp-server-trello'

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