import { ConfigurationError } from './errors.js';
import { logger } from './logger.js';
import { dataAccess } from '../db/supabase.js';
interface ConfigValidation {
isValid: boolean;
errors: string[];
warnings: string[];
config: {
hasSupabaseUrl: boolean;
hasSupabaseAnonKey: boolean;
hasSupabaseServiceKey: boolean;
hasPerplexityApiKey: boolean;
isDevelopment: boolean;
logLevel: string;
};
}
/**
* Validates the server configuration and environment variables
*/
export async function validateConfiguration(): Promise<ConfigValidation> {
const errors: string[] = [];
const warnings: string[] = [];
const config = {
hasSupabaseUrl: !!process.env.SUPABASE_URL,
hasSupabaseAnonKey: !!process.env.SUPABASE_ANON_KEY,
hasSupabaseServiceKey: !!process.env.SUPABASE_SERVICE_KEY,
hasPerplexityApiKey: !!process.env.PERPLEXITY_API_KEY,
isDevelopment: process.env.NODE_ENV !== 'production',
logLevel: process.env.LOG_LEVEL || 'INFO'
};
// Required environment variables
if (!config.hasSupabaseUrl) {
errors.push('SUPABASE_URL is required. Please set it in your environment variables.');
}
if (!config.hasSupabaseAnonKey) {
errors.push('SUPABASE_ANON_KEY is required. Please set it in your environment variables.');
}
if (!config.hasPerplexityApiKey) {
errors.push('PERPLEXITY_API_KEY is required for Dutch market validation. Please set it in your environment variables.');
}
// Optional but recommended
if (!config.hasSupabaseServiceKey) {
warnings.push('SUPABASE_SERVICE_KEY not found. Some administrative features will be disabled.');
}
// Validate Supabase URL format
if (config.hasSupabaseUrl) {
try {
const url = new URL(process.env.SUPABASE_URL!);
if (!url.hostname.includes('supabase')) {
warnings.push('SUPABASE_URL does not appear to be a Supabase URL. Ensure it\'s correct.');
}
} catch (error) {
errors.push('SUPABASE_URL is not a valid URL format.');
}
}
// Check if keys look valid (basic format check)
if (config.hasSupabaseAnonKey && process.env.SUPABASE_ANON_KEY!.length < 30) {
warnings.push('SUPABASE_ANON_KEY appears to be too short. Ensure it\'s a valid key.');
}
return {
isValid: errors.length === 0,
errors,
warnings,
config
};
}
/**
* Tests database connectivity
*/
export async function testDatabaseConnection(): Promise<{
isConnected: boolean;
error?: string;
latencyMs?: number;
}> {
const startTime = Date.now();
try {
const isHealthy = await dataAccess.checkDatabaseHealth();
const latencyMs = Date.now() - startTime;
if (!isHealthy) {
return {
isConnected: false,
error: 'Database health check failed',
latencyMs
};
}
return {
isConnected: true,
latencyMs
};
} catch (error) {
return {
isConnected: false,
error: `Connection test failed: ${(error as Error).message}`,
latencyMs: Date.now() - startTime
};
}
}
/**
* Validates the complete server setup
*/
export async function validateServerSetup(): Promise<void> {
logger.info('Validating server configuration...');
// Step 1: Validate configuration
const configValidation = await validateConfiguration();
// Log warnings
configValidation.warnings.forEach(warning => {
logger.warn(warning);
});
// Check for critical errors
if (!configValidation.isValid) {
const errorMessage = 'Server configuration is invalid:\n' +
configValidation.errors.map(e => ` - ${e}`).join('\n');
throw new ConfigurationError(errorMessage, {
errors: configValidation.errors,
config: configValidation.config
});
}
logger.info('Configuration validation passed', configValidation.config);
// Step 2: Test database connection
logger.info('Testing database connection...');
const dbTest = await testDatabaseConnection();
if (!dbTest.isConnected) {
throw new ConfigurationError(
`Database connection failed: ${dbTest.error}\n\n` +
'Please ensure:\n' +
'1. Your Supabase project is active\n' +
'2. The SUPABASE_URL and SUPABASE_ANON_KEY are correct\n' +
'3. Your network can reach Supabase servers',
{
error: dbTest.error,
latencyMs: dbTest.latencyMs
}
);
}
logger.info('Database connection successful', { latencyMs: dbTest.latencyMs });
// Step 3: Log server capabilities
logger.info('Server capabilities', {
hasAdminAccess: configValidation.config.hasSupabaseServiceKey,
hasDutchValidation: configValidation.config.hasPerplexityApiKey,
environment: configValidation.config.isDevelopment ? 'development' : 'production',
logLevel: configValidation.config.logLevel
});
}
/**
* Get setup instructions for missing configuration
*/
export function getSetupInstructions(): string {
return `
MCP Server ROI - Setup Instructions
===================================
1. Create a Supabase project at https://supabase.com
2. Set up your database:
- Go to SQL Editor in your Supabase dashboard
- Run the schema files in order:
a) database/schema.sql
b) database/001_security_update.sql
c) database/002_transactional_functions.sql
3. Get your API credentials:
- Go to Settings > API in your Supabase dashboard
- Copy the Project URL and anon/public key
4. Set environment variables:
export SUPABASE_URL="your-project-url"
export SUPABASE_ANON_KEY="your-anon-key"
export SUPABASE_SERVICE_KEY="your-service-key" # Optional but recommended
export PERPLEXITY_API_KEY="your-perplexity-key" # Required for Dutch market validation
5. For production, also set:
export NODE_ENV="production"
export LOG_LEVEL="INFO"
6. Add to Claude Desktop config:
{
"mcpServers": {
"roi": {
"command": "node",
"args": ["/path/to/mcp-server-roi/dist/index.js"],
"env": {
"SUPABASE_URL": "your-project-url",
"SUPABASE_ANON_KEY": "your-anon-key",
"PERPLEXITY_API_KEY": "your-perplexity-key"
}
}
}
}
For more information, see the README.md file.
`;
}