Skip to main content
Glama
dj-pearson

Supabase Coolify MCP Server

by dj-pearson
diagnose.ts17.8 kB
#!/usr/bin/env node /** * Diagnostic Tool for Supabase Coolify MCP Server * * This script tests connectivity to Coolify and Supabase services * and provides detailed diagnostics to help troubleshoot connection issues. */ import dotenv from 'dotenv'; import axios from 'axios'; import { createClient } from '@supabase/supabase-js'; import * as fs from 'fs'; import * as path from 'path'; // Color codes for terminal output const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', }; interface DiagnosticResult { name: string; status: 'pass' | 'fail' | 'warning' | 'skip'; message: string; details?: any; suggestion?: string; } class Diagnostics { private results: DiagnosticResult[] = []; constructor() {} log(color: keyof typeof colors, message: string) { console.log(`${colors[color]}${message}${colors.reset}`); } addResult(result: DiagnosticResult) { this.results.push(result); } printResult(result: DiagnosticResult) { const icon = { pass: '✅', fail: '❌', warning: '⚠️ ', skip: '⏭️ ', }[result.status]; const color = { pass: 'green', fail: 'red', warning: 'yellow', skip: 'blue', }[result.status] as keyof typeof colors; this.log(color, `${icon} ${result.name}: ${result.message}`); if (result.details) { console.log(' Details:', result.details); } if (result.suggestion) { this.log('cyan', ` 💡 Suggestion: ${result.suggestion}`); } console.log(); } printSummary() { const passed = this.results.filter(r => r.status === 'pass').length; const failed = this.results.filter(r => r.status === 'fail').length; const warnings = this.results.filter(r => r.status === 'warning').length; console.log('═'.repeat(70)); this.log('bright', '\n📊 DIAGNOSTIC SUMMARY\n'); this.log('green', `✅ Passed: ${passed}`); this.log('red', `❌ Failed: ${failed}`); this.log('yellow', `⚠️ Warnings: ${warnings}`); this.log('blue', `⏭️ Skipped: ${this.results.filter(r => r.status === 'skip').length}`); console.log(); if (failed > 0) { this.log('red', '🔴 CRITICAL ISSUES DETECTED - MCP Server will not work properly'); } else if (warnings > 0) { this.log('yellow', '🟡 WARNING - MCP Server may have limited functionality'); } else { this.log('green', '🟢 ALL CHECKS PASSED - MCP Server should work correctly'); } console.log(); } async run() { this.log('bright', '\n═══════════════════════════════════════════════════════════════════'); this.log('cyan', '🔍 SUPABASE COOLIFY MCP SERVER DIAGNOSTICS'); this.log('bright', '═══════════════════════════════════════════════════════════════════\n'); // 1. Check .env file await this.checkEnvFile(); // 2. Load environment variables dotenv.config(); // 3. Check required environment variables await this.checkEnvironmentVariables(); // 4. Test Coolify connection await this.testCoolifyConnection(); // 5. Test Coolify authentication await this.testCoolifyAuthentication(); // 6. Test Supabase connection await this.testSupabaseConnection(); // 7. Test Supabase authentication await this.testSupabaseAuthentication(); // 8. Test Supabase services await this.testSupabaseServices(); // 9. Check network connectivity await this.checkNetworkConnectivity(); // Print all results console.log('\n═══════════════════════════════════════════════════════════════════'); this.log('bright', '📋 DETAILED RESULTS'); console.log('═══════════════════════════════════════════════════════════════════\n'); this.results.forEach(result => this.printResult(result)); // Print summary this.printSummary(); } async checkEnvFile() { const envPath = path.join(process.cwd(), '.env'); const envExamplePath = path.join(process.cwd(), 'env.example'); if (fs.existsSync(envPath)) { this.addResult({ name: '.env File', status: 'pass', message: '.env file found', details: { path: envPath }, }); } else { this.addResult({ name: '.env File', status: 'fail', message: '.env file not found', details: { expected_path: envPath, example_exists: fs.existsSync(envExamplePath) }, suggestion: fs.existsSync(envExamplePath) ? 'Copy env.example to .env: cp env.example .env' : 'Create a .env file with required environment variables', }); } } async checkEnvironmentVariables() { const required = [ { name: 'COOLIFY_API_URL', example: 'http://localhost:8000 or https://coolify.example.com' }, { name: 'COOLIFY_API_TOKEN', example: 'clf_xxxxxxxxxxxxx (from Coolify dashboard)' }, { name: 'SUPABASE_URL', example: 'https://your-project.supabase.co' }, { name: 'SUPABASE_SERVICE_ROLE_KEY', example: 'eyJhbGc... (service role key, not anon key)' }, ]; for (const env of required) { const value = process.env[env.name]; if (!value) { this.addResult({ name: `ENV: ${env.name}`, status: 'fail', message: 'Not set', details: { example: env.example }, suggestion: `Add ${env.name} to your .env file`, }); } else if (value.includes('your-') || value.includes('example') || value.includes('here')) { this.addResult({ name: `ENV: ${env.name}`, status: 'fail', message: 'Contains placeholder value', details: { current_value: value.substring(0, 30) + '...' }, suggestion: `Replace placeholder with actual value for ${env.name}`, }); } else { const masked = this.maskSensitive(value); this.addResult({ name: `ENV: ${env.name}`, status: 'pass', message: 'Set correctly', details: { value: masked }, }); } } } async testCoolifyConnection() { const apiUrl = process.env.COOLIFY_API_URL; if (!apiUrl) { this.addResult({ name: 'Coolify Connection', status: 'skip', message: 'Skipped - COOLIFY_API_URL not set', }); return; } try { const url = new URL(apiUrl); // Test basic connectivity const response = await axios.get(`${apiUrl}/api/v1/health`, { timeout: 10000, validateStatus: () => true, // Accept any status code }); if (response.status === 200) { this.addResult({ name: 'Coolify Connection', status: 'pass', message: 'Successfully connected to Coolify', details: { url: apiUrl, status: response.status, response_time: `${response.headers['x-response-time'] || 'N/A'}`, }, }); } else if (response.status === 401 || response.status === 403) { this.addResult({ name: 'Coolify Connection', status: 'pass', message: 'Connected to Coolify (authentication will be tested separately)', details: { url: apiUrl, status: response.status }, }); } else if (response.status === 404) { this.addResult({ name: 'Coolify Connection', status: 'warning', message: 'Connected but /health endpoint not found (may still work)', details: { url: apiUrl, status: response.status }, }); } else { this.addResult({ name: 'Coolify Connection', status: 'warning', message: `Unexpected response: ${response.status}`, details: { url: apiUrl, status: response.status }, }); } } catch (error: any) { let suggestion = ''; if (error.code === 'ECONNREFUSED') { suggestion = 'Check if Coolify is running and accessible at this URL'; } else if (error.code === 'ENOTFOUND') { suggestion = 'Check if the hostname is correct and DNS is resolving'; } else if (error.code === 'ETIMEDOUT') { suggestion = 'Check firewall settings and network connectivity'; } else if (error.message.includes('certificate')) { suggestion = 'SSL certificate issue - check HTTPS configuration'; } this.addResult({ name: 'Coolify Connection', status: 'fail', message: 'Failed to connect', details: { url: apiUrl, error: error.message, code: error.code, }, suggestion: suggestion || 'Verify COOLIFY_API_URL is correct and accessible', }); } } async testCoolifyAuthentication() { const apiUrl = process.env.COOLIFY_API_URL; const apiToken = process.env.COOLIFY_API_TOKEN; if (!apiUrl || !apiToken) { this.addResult({ name: 'Coolify Authentication', status: 'skip', message: 'Skipped - credentials not set', }); return; } try { const response = await axios.get(`${apiUrl}/api/v1/applications`, { headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); this.addResult({ name: 'Coolify Authentication', status: 'pass', message: 'Successfully authenticated with Coolify', details: { applications_found: Array.isArray(response.data) ? response.data.length : 'N/A', }, }); } catch (error: any) { if (error.response?.status === 401) { this.addResult({ name: 'Coolify Authentication', status: 'fail', message: 'Invalid API token', details: { status: 401 }, suggestion: 'Generate a new API token from Coolify: Settings > API Tokens', }); } else if (error.response?.status === 403) { this.addResult({ name: 'Coolify Authentication', status: 'fail', message: 'API token lacks required permissions', details: { status: 403 }, suggestion: 'Ensure the API token has sufficient permissions', }); } else { this.addResult({ name: 'Coolify Authentication', status: 'fail', message: 'Authentication test failed', details: { error: error.response?.data?.message || error.message, status: error.response?.status, }, suggestion: 'Verify COOLIFY_API_TOKEN is correct', }); } } } async testSupabaseConnection() { const supabaseUrl = process.env.SUPABASE_URL; if (!supabaseUrl) { this.addResult({ name: 'Supabase Connection', status: 'skip', message: 'Skipped - SUPABASE_URL not set', }); return; } try { const response = await axios.get(`${supabaseUrl}/rest/v1/`, { timeout: 10000, validateStatus: () => true, }); if (response.status === 200 || response.status === 401) { this.addResult({ name: 'Supabase Connection', status: 'pass', message: 'Successfully connected to Supabase', details: { url: supabaseUrl, status: response.status, version: response.headers['x-supabase-version'] || 'unknown', }, }); } else { this.addResult({ name: 'Supabase Connection', status: 'warning', message: `Unexpected response: ${response.status}`, details: { url: supabaseUrl, status: response.status }, }); } } catch (error: any) { let suggestion = ''; if (error.code === 'ECONNREFUSED') { suggestion = 'Check if Supabase is running and accessible'; } else if (error.code === 'ENOTFOUND') { suggestion = 'Check if the Supabase URL is correct'; } else if (error.code === 'ETIMEDOUT') { suggestion = 'Check firewall and network settings'; } this.addResult({ name: 'Supabase Connection', status: 'fail', message: 'Failed to connect', details: { url: supabaseUrl, error: error.message, code: error.code, }, suggestion: suggestion || 'Verify SUPABASE_URL is correct', }); } } async testSupabaseAuthentication() { const supabaseUrl = process.env.SUPABASE_URL; const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY; if (!supabaseUrl || !serviceRoleKey) { this.addResult({ name: 'Supabase Authentication', status: 'skip', message: 'Skipped - credentials not set', }); return; } try { const client = createClient(supabaseUrl, serviceRoleKey, { auth: { autoRefreshToken: false, persistSession: false, }, }); // Try to query a system table const { data, error } = await client .from('pg_tables') .select('schemaname') .limit(1); if (error) { if (error.message.includes('JWT') || error.message.includes('401')) { this.addResult({ name: 'Supabase Authentication', status: 'fail', message: 'Invalid service role key', details: { error: error.message }, suggestion: 'Verify SUPABASE_SERVICE_ROLE_KEY (not anon key!)', }); } else { this.addResult({ name: 'Supabase Authentication', status: 'warning', message: 'Auth successful but query failed', details: { error: error.message }, }); } } else { this.addResult({ name: 'Supabase Authentication', status: 'pass', message: 'Successfully authenticated with Supabase', details: { can_query_database: true }, }); } } catch (error: any) { this.addResult({ name: 'Supabase Authentication', status: 'fail', message: 'Authentication failed', details: { error: error.message }, suggestion: 'Check SUPABASE_SERVICE_ROLE_KEY value', }); } } async testSupabaseServices() { const supabaseUrl = process.env.SUPABASE_URL; const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY; if (!supabaseUrl || !serviceRoleKey) { this.addResult({ name: 'Supabase Services', status: 'skip', message: 'Skipped - credentials not set', }); return; } const services = [ { name: 'REST API', endpoint: '/rest/v1/' }, { name: 'Auth', endpoint: '/auth/v1/health' }, { name: 'Storage', endpoint: '/storage/v1/healthcheck' }, { name: 'Realtime', endpoint: '/realtime/v1/health' }, ]; const results = await Promise.all( services.map(async (service) => { try { const response = await axios.get(`${supabaseUrl}${service.endpoint}`, { headers: { 'Authorization': `Bearer ${serviceRoleKey}`, 'apikey': serviceRoleKey, }, timeout: 5000, validateStatus: () => true, }); return { service: service.name, status: response.status >= 200 && response.status < 400 ? 'healthy' : 'unhealthy', statusCode: response.status, }; } catch { return { service: service.name, status: 'error', statusCode: null, }; } }) ); const allHealthy = results.every(r => r.status === 'healthy'); const anyHealthy = results.some(r => r.status === 'healthy'); this.addResult({ name: 'Supabase Services', status: allHealthy ? 'pass' : anyHealthy ? 'warning' : 'fail', message: allHealthy ? 'All services are healthy' : anyHealthy ? 'Some services are not responding' : 'No services are responding', details: results.reduce((acc, r) => { acc[r.service] = r.status === 'healthy' ? '✓' : '✗'; return acc; }, {} as Record<string, string>), }); } async checkNetworkConnectivity() { try { // Test general internet connectivity await axios.get('https://www.google.com', { timeout: 5000 }); this.addResult({ name: 'Network Connectivity', status: 'pass', message: 'Internet connection is working', }); } catch { this.addResult({ name: 'Network Connectivity', status: 'warning', message: 'Unable to verify internet connection', suggestion: 'Check your network connection', }); } } maskSensitive(value: string): string { if (value.length <= 8) { return '***'; } return value.substring(0, 8) + '...' + value.substring(value.length - 4); } } // Run diagnostics const diagnostics = new Diagnostics(); diagnostics.run().catch(error => { console.error('Diagnostic script failed:', 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/dj-pearson/supabase-coolify-mcp-server'

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