Skip to main content
Glama
dj-pearson

Supabase Coolify MCP Server

by dj-pearson
health-check.ts11.3 kB
/** * Health check system for verifying Coolify and Supabase connectivity * Runs on startup and provides a verification tool */ import { CoolifyClient } from './coolify-client.js'; import { SupabaseManager } from './supabase-client.js'; import { SupabaseCLI } from './supabase-cli.js'; import type { ToolResponse } from './types.js'; export interface HealthCheckResult { service: string; status: 'healthy' | 'unhealthy' | 'warning' | 'unknown'; message: string; details?: any; timestamp: string; } export interface SystemHealthReport { overall_status: 'healthy' | 'degraded' | 'unhealthy'; checks: HealthCheckResult[]; timestamp: string; summary: { total: number; healthy: number; unhealthy: number; warnings: number; }; } export class HealthChecker { constructor( private coolify: CoolifyClient, private supabase: SupabaseManager, private supabaseCLI: SupabaseCLI ) {} /** * Run all health checks */ async checkAll(): Promise<SystemHealthReport> { const checks: HealthCheckResult[] = []; // Run all checks in parallel const results = await Promise.allSettled([ this.checkCoolifyConnection(), this.checkCoolifyAuth(), this.checkSupabaseConnection(), this.checkSupabaseAuth(), this.checkSupabaseDatabase(), this.checkSupabaseCLI(), ]); // Collect results results.forEach((result) => { if (result.status === 'fulfilled') { checks.push(result.value); } else { checks.push({ service: 'Unknown', status: 'unhealthy', message: `Check failed: ${result.reason}`, timestamp: new Date().toISOString(), }); } }); // Calculate summary const summary = { total: checks.length, healthy: checks.filter(c => c.status === 'healthy').length, unhealthy: checks.filter(c => c.status === 'unhealthy').length, warnings: checks.filter(c => c.status === 'warning').length, }; // Determine overall status let overall_status: 'healthy' | 'degraded' | 'unhealthy'; if (summary.unhealthy > 0) { overall_status = 'unhealthy'; } else if (summary.warnings > 0) { overall_status = 'degraded'; } else { overall_status = 'healthy'; } return { overall_status, checks, timestamp: new Date().toISOString(), summary, }; } /** * Check Coolify connection */ private async checkCoolifyConnection(): Promise<HealthCheckResult> { const start = Date.now(); try { const result = await this.coolify.listApplications(); const duration = Date.now() - start; if (result.success) { return { service: 'Coolify Connection', status: 'healthy', message: `Connected successfully (${duration}ms)`, details: { response_time: duration, applications_count: result.data?.length || 0 }, timestamp: new Date().toISOString(), }; } else { return { service: 'Coolify Connection', status: 'unhealthy', message: result.error || 'Failed to connect', timestamp: new Date().toISOString(), }; } } catch (error) { return { service: 'Coolify Connection', status: 'unhealthy', message: error instanceof Error ? error.message : 'Connection failed', timestamp: new Date().toISOString(), }; } } /** * Check Coolify authentication */ private async checkCoolifyAuth(): Promise<HealthCheckResult> { try { const result = await this.coolify.listApplications(); if (result.success) { return { service: 'Coolify Authentication', status: 'healthy', message: 'API token is valid', timestamp: new Date().toISOString(), }; } else if (result.error?.includes('401') || result.error?.includes('Unauthorized')) { return { service: 'Coolify Authentication', status: 'unhealthy', message: 'Invalid API token or expired', details: { suggestion: 'Generate a new API token from Coolify dashboard' }, timestamp: new Date().toISOString(), }; } else { return { service: 'Coolify Authentication', status: 'warning', message: 'Unable to verify authentication', timestamp: new Date().toISOString(), }; } } catch (error) { return { service: 'Coolify Authentication', status: 'unhealthy', message: error instanceof Error ? error.message : 'Auth check failed', timestamp: new Date().toISOString(), }; } } /** * Check Supabase connection */ private async checkSupabaseConnection(): Promise<HealthCheckResult> { const start = Date.now(); try { const result = await this.supabase.getVersion(); const duration = Date.now() - start; if (result.success) { return { service: 'Supabase Connection', status: 'healthy', message: `Connected successfully (${duration}ms)`, details: { response_time: duration, version: result.data }, timestamp: new Date().toISOString(), }; } else { return { service: 'Supabase Connection', status: 'unhealthy', message: result.error || 'Failed to connect', timestamp: new Date().toISOString(), }; } } catch (error) { return { service: 'Supabase Connection', status: 'unhealthy', message: error instanceof Error ? error.message : 'Connection failed', timestamp: new Date().toISOString(), }; } } /** * Check Supabase authentication */ private async checkSupabaseAuth(): Promise<HealthCheckResult> { try { const result = await this.supabase.listMigrations(); if (result.success) { return { service: 'Supabase Authentication', status: 'healthy', message: 'Service role key is valid', timestamp: new Date().toISOString(), }; } else if (result.error?.includes('401') || result.error?.includes('JWT')) { return { service: 'Supabase Authentication', status: 'unhealthy', message: 'Invalid service role key or expired JWT', details: { suggestion: 'Check SUPABASE_SERVICE_ROLE_KEY in environment' }, timestamp: new Date().toISOString(), }; } else { return { service: 'Supabase Authentication', status: 'warning', message: 'Unable to verify authentication', timestamp: new Date().toISOString(), }; } } catch (error) { return { service: 'Supabase Authentication', status: 'unhealthy', message: error instanceof Error ? error.message : 'Auth check failed', timestamp: new Date().toISOString(), }; } } /** * Check Supabase database access */ private async checkSupabaseDatabase(): Promise<HealthCheckResult> { try { const result = await this.supabase.executeSQL('SELECT 1 as test;'); if (result.success) { return { service: 'Supabase Database', status: 'healthy', message: 'Database is accessible', timestamp: new Date().toISOString(), }; } else { return { service: 'Supabase Database', status: 'warning', message: 'Database access issue (migrations may still work)', details: { error: result.error }, timestamp: new Date().toISOString(), }; } } catch { return { service: 'Supabase Database', status: 'warning', message: 'Unable to verify database (migrations may still work)', timestamp: new Date().toISOString(), }; } } /** * Check Supabase CLI availability */ private async checkSupabaseCLI(): Promise<HealthCheckResult> { try { const result = await this.supabaseCLI.checkCLIInstalled(); if (result.success && result.data?.installed) { return { service: 'Supabase CLI', status: 'healthy', message: `CLI installed (${result.data.version})`, details: { version: result.data.version }, timestamp: new Date().toISOString(), }; } else { return { service: 'Supabase CLI', status: 'warning', message: 'CLI not installed (CLI tools will not work)', details: { suggestion: 'Install with: npm install -g supabase' }, timestamp: new Date().toISOString(), }; } } catch { return { service: 'Supabase CLI', status: 'warning', message: 'CLI not available (CLI tools will not work)', timestamp: new Date().toISOString(), }; } } /** * Quick startup check - only critical services */ async quickCheck(): Promise<{ healthy: boolean; message: string; issues: string[] }> { const issues: string[] = []; // Check Coolify const coolifyResult = await this.coolify.listApplications(); if (!coolifyResult.success) { issues.push(`Coolify: ${coolifyResult.error}`); } // Check Supabase const supabaseResult = await this.supabase.getVersion(); if (!supabaseResult.success) { issues.push(`Supabase: ${supabaseResult.error}`); } const healthy = issues.length === 0; const message = healthy ? 'All critical services are healthy' : `Found ${issues.length} issue(s) with critical services`; return { healthy, message, issues }; } } /** * Run startup health check and log results */ export async function runStartupHealthCheck( coolify: CoolifyClient, supabase: SupabaseManager, supabaseCLI: SupabaseCLI ): Promise<boolean> { console.error('\n🔍 Running startup health checks...\n'); const checker = new HealthChecker(coolify, supabase, supabaseCLI); const quickResult = await checker.quickCheck(); if (quickResult.healthy) { console.error('✅ All critical services are healthy\n'); return true; } else { console.error('⚠️ Issues detected:\n'); quickResult.issues.forEach(issue => { console.error(` ❌ ${issue}`); }); console.error('\n⚠️ Some services are not available. The server will start but some tools may not work.'); console.error(' Run the "verify_setup" tool for detailed diagnostics.\n'); return false; } } /** * Create a verification tool response */ export async function createVerificationReport( coolify: CoolifyClient, supabase: SupabaseManager, supabaseCLI: SupabaseCLI ): Promise<ToolResponse<SystemHealthReport>> { const checker = new HealthChecker(coolify, supabase, supabaseCLI); const report = await checker.checkAll(); return { success: report.overall_status !== 'unhealthy', data: report, message: report.overall_status === 'healthy' ? 'All systems operational' : report.overall_status === 'degraded' ? 'Some services have warnings' : 'Critical services are unavailable', }; }

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/dj-pearson/supabase-coolify-mcp-server'

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