UNPKG

@spaik/mcp-server-roi

Version:

MCP server for AI ROI prediction and tracking with Monte Carlo simulations

136 lines 5.64 kB
import { createClient } from '@supabase/supabase-js'; import * as dotenv from 'dotenv'; import { logger } from '../utils/logger.js'; import { DatabaseError, ConfigurationError } from '../utils/errors.js'; dotenv.config(); // Validate environment variables if (!process.env.SUPABASE_URL || !process.env.SUPABASE_ANON_KEY) { const error = new ConfigurationError('Missing Supabase environment variables. Please set SUPABASE_URL and SUPABASE_ANON_KEY', { hasUrl: !!process.env.SUPABASE_URL, hasAnonKey: !!process.env.SUPABASE_ANON_KEY }); logger.error('Failed to initialize Supabase client', error); throw error; } logger.info('Initializing Supabase clients', { hasServiceKey: !!process.env.SUPABASE_SERVICE_KEY }); // Public client for user operations (with RLS) export const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY, { auth: { persistSession: false } }); // Admin client for server-side operations (internal use only) const supabaseAdmin = process.env.SUPABASE_SERVICE_KEY ? createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY, { auth: { persistSession: false } }) : null; if (!supabaseAdmin) { logger.warn('Admin client not available - some features may be limited'); } // Data access layer - all admin operations should go through these functions export const dataAccess = { // System-level operations that bypass RLS (use with caution) async systemQuery(query) { if (!supabaseAdmin) { throw new ConfigurationError('Admin client not available - service key required for system operations', { operation: 'systemQuery' }); } try { logger.debug('Executing system query'); const result = await query(); logger.debug('System query completed successfully'); return result; } catch (error) { logger.error('System query failed', error); throw new DatabaseError(`System query failed: ${error.message}`, { originalError: error.message }); } }, // Migrate existing data to include user_id async migrateProjectOwnership(projectId, userId) { if (!supabaseAdmin) { throw new ConfigurationError('Admin client required for migration', { operation: 'migrateProjectOwnership' }); } logger.info('Migrating project ownership', { projectId, userId }); try { const { error } = await supabaseAdmin .from('projects') .update({ user_id: userId }) .eq('id', projectId); if (error) { throw new DatabaseError(`Failed to migrate project ownership: ${error.message}`, { projectId, userId, code: error.code }); } logger.info('Project ownership migrated successfully', { projectId, userId }); } catch (error) { if (error instanceof DatabaseError) throw error; logger.error('Unexpected error during migration', error); throw new DatabaseError(`Migration failed: ${error.message}`, { projectId, userId }); } }, // System health checks async checkDatabaseHealth() { const client = supabaseAdmin || supabase; try { logger.debug('Checking database health'); const { error } = await client .from('projects') .select('count') .limit(0); if (error) { logger.error('Database health check failed', error); return false; } logger.debug('Database health check passed'); return true; } catch (error) { logger.error('Unexpected error during health check', error); return false; } }, // Get anonymous statistics (no user data) async getSystemStats() { if (!supabaseAdmin) { throw new ConfigurationError('Admin client required for system stats', { operation: 'getSystemStats' }); } logger.debug('Fetching system statistics'); try { const { data, error } = await supabaseAdmin .from('projects') .select('industry, status, created_at'); if (error) { throw new DatabaseError(`Failed to fetch system stats: ${error.message}`, { code: error.code }); } // Return anonymized statistics only const stats = { totalProjects: data?.length || 0, byIndustry: data?.reduce((acc, p) => { acc[p.industry] = (acc[p.industry] || 0) + 1; return acc; }, {}) || {}, byStatus: data?.reduce((acc, p) => { acc[p.status] = (acc[p.status] || 0) + 1; return acc; }, {}) || {} }; logger.debug('System statistics fetched', { totalProjects: stats.totalProjects }); return stats; } catch (error) { if (error instanceof DatabaseError) throw error; logger.error('Unexpected error fetching stats', error); throw new DatabaseError(`Failed to get system stats: ${error.message}`, {}); } } }; // Export the appropriate client for MCP server operations // MCP servers run server-side and should use the admin client when available export const mcpDb = supabaseAdmin || supabase; //# sourceMappingURL=supabase.js.map