UNPKG

tryaii-mcp-server

Version:

TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence

209 lines • 10.7 kB
import dotenv from 'dotenv'; // šŸ—ļø CLEAN CONFIGURATION LOADING // Load configurations in order of precedence: // 1. Server configuration (infrastructure only) // 2. User API keys (optional, user-provided) // 3. TryAII fallback keys (secure, internal) // Load config files if they exist (silent fail for production deployments) if (process.env.NODE_ENV !== 'production') { try { dotenv.config({ path: 'config/server.env' }); } catch { } try { dotenv.config({ path: 'config/user-keys.env' }); } catch { } try { dotenv.config({ path: 'config/tryaii-keys.env' }); } catch { } } // Load production env file if it exists try { dotenv.config({ path: '.env.production' }); } catch { } const getEnvVar = (key, defaultValue, required = false) => { const value = process.env[key] || defaultValue; if (required && !value) { throw new Error(`Required environment variable ${key} is not set`); } return value || ''; }; const getEnvNumber = (key, defaultValue) => { const value = process.env[key]; if (!value) return defaultValue; const parsed = parseInt(value, 10); if (isNaN(parsed)) { throw new Error(`Environment variable ${key} must be a valid number`); } return parsed; }; // šŸ”‘ Smart API key resolution with clear precedence const resolveApiKeys = () => { return { user: { ...(process.env.USER_OPENAI_API_KEY && process.env.USER_OPENAI_API_KEY !== 'your_openai_key_here' && { openai: process.env.USER_OPENAI_API_KEY }), ...(process.env.USER_ANTHROPIC_API_KEY && process.env.USER_ANTHROPIC_API_KEY !== 'your_anthropic_key_here' && { anthropic: process.env.USER_ANTHROPIC_API_KEY }), ...(process.env.USER_GOOGLE_API_KEY && process.env.USER_GOOGLE_API_KEY !== 'your_google_key_here' && { google: process.env.USER_GOOGLE_API_KEY }), ...(process.env.USER_DEEPSEEK_API_KEY && process.env.USER_DEEPSEEK_API_KEY !== 'your_deepseek_key_here' && { deepseek: process.env.USER_DEEPSEEK_API_KEY }), ...(process.env.USER_XAI_API_KEY && process.env.USER_XAI_API_KEY !== 'your_xai_key_here' && { xai: process.env.USER_XAI_API_KEY }), ...(process.env.USER_MISTRAL_API_KEY && process.env.USER_MISTRAL_API_KEY !== 'your_mistral_key_here' && { mistral: process.env.USER_MISTRAL_API_KEY }), ...(process.env.USER_BRAVE_API_KEY && process.env.USER_BRAVE_API_KEY !== 'your_brave_key_here' && { brave: process.env.USER_BRAVE_API_KEY }) }, tryaii: { ...(process.env.TRYAII_OPENAI_API_KEY && process.env.TRYAII_OPENAI_API_KEY !== 'your-tryaii-openai-key-here' && { openai: process.env.TRYAII_OPENAI_API_KEY }), ...(process.env.TRYAII_ANTHROPIC_API_KEY && process.env.TRYAII_ANTHROPIC_API_KEY !== 'your-tryaii-anthropic-key-here' && { anthropic: process.env.TRYAII_ANTHROPIC_API_KEY }), ...(process.env.TRYAII_GOOGLE_API_KEY && process.env.TRYAII_GOOGLE_API_KEY !== 'your-tryaii-google-key-here' && { google: process.env.TRYAII_GOOGLE_API_KEY }), ...(process.env.TRYAII_DEEPSEEK_API_KEY && process.env.TRYAII_DEEPSEEK_API_KEY !== 'your-tryaii-deepseek-key-here' && { deepseek: process.env.TRYAII_DEEPSEEK_API_KEY }), ...(process.env.TRYAII_XAI_API_KEY && process.env.TRYAII_XAI_API_KEY !== 'your-tryaii-xai-key-here' && { xai: process.env.TRYAII_XAI_API_KEY }), ...(process.env.TRYAII_MISTRAL_API_KEY && process.env.TRYAII_MISTRAL_API_KEY !== 'your-tryaii-mistral-key-here' && { mistral: process.env.TRYAII_MISTRAL_API_KEY }), ...(process.env.TRYAII_BRAVE_API_KEY && process.env.TRYAII_BRAVE_API_KEY !== 'your-tryaii-brave-key-here' && { brave: process.env.TRYAII_BRAVE_API_KEY }) } }; }; // šŸ”§ Generate MCP Process Environment Variables const generateMcpEnvironment = (apiKeys) => { const env = {}; // Use user keys first (priority), then fallback to TryAII keys const resolveKey = (provider) => { return apiKeys.user[provider] || apiKeys.tryaii[provider]; }; // Map to MCP environment variable names const openaiKey = resolveKey('openai'); if (openaiKey) env.OPENAI_API_KEY = openaiKey; const anthropicKey = resolveKey('anthropic'); if (anthropicKey) env.ANTHROPIC_API_KEY = anthropicKey; const googleKey = resolveKey('google'); if (googleKey) env.GOOGLE_API_KEY = googleKey; const deepseekKey = resolveKey('deepseek'); if (deepseekKey) env.DEEPSEEK_API_KEY = deepseekKey; const xaiKey = resolveKey('xai'); if (xaiKey) env.XAI_API_KEY = xaiKey; const mistralKey = resolveKey('mistral'); if (mistralKey) env.MISTRAL_API_KEY = mistralKey; const braveKey = resolveKey('brave'); if (braveKey) env.BRAVE_API_KEY = braveKey; return env; }; export const config = { server: { port: getEnvNumber('PORT', getEnvNumber('MIDDLEWARE_PORT', 3001)), host: getEnvVar('MIDDLEWARE_HOST', '0.0.0.0'), nodeEnv: getEnvVar('NODE_ENV', 'development'), corsOrigins: getEnvVar('CORS_ORIGINS', '*').split(','), modes: { http: getEnvVar('SERVER_MODE_HTTP', 'true') === 'true', mcp: getEnvVar('SERVER_MODE_MCP', 'true') === 'true', ...(process.env.MCP_PROTOCOL_PORT && { mcpPort: getEnvNumber('MCP_PROTOCOL_PORT', 3002) }) } }, mcp: { executablePath: getEnvVar('MCP_EXECUTABLE_PATH', '', false), // Optional for npm package timeout: getEnvNumber('MCP_PROCESS_TIMEOUT', 30000), maxRestarts: getEnvNumber('MCP_MAX_RESTARTS', 5), restartDelay: 1000, env: generateMcpEnvironment(resolveApiKeys()) // šŸ”§ Dynamic API key resolution }, http: { baseURL: getEnvVar('MCP_HTTP_BASE_URL', getEnvVar('NODE_ENV', 'development') === 'production' ? 'https://tryaii.onrender.com' : 'http://localhost:4000'), timeout: getEnvNumber('HTTP_CLIENT_TIMEOUT', 30000), maxRetries: getEnvNumber('HTTP_CLIENT_MAX_RETRIES', 3), retryDelay: getEnvNumber('HTTP_CLIENT_RETRY_DELAY', 1000), maxConcurrentRequests: getEnvNumber('HTTP_CLIENT_MAX_CONCURRENT', 10), healthCheckInterval: getEnvNumber('HTTP_CLIENT_HEALTH_CHECK_INTERVAL', 30000) }, session: { timeoutMinutes: getEnvNumber('SESSION_TIMEOUT_MINUTES', 60), cleanupIntervalMinutes: getEnvNumber('SESSION_CLEANUP_INTERVAL_MINUTES', 5), maxConcurrentSessions: getEnvNumber('MAX_CONCURRENT_SESSIONS', 100) }, logging: { level: getEnvVar('LOG_LEVEL', 'info'), file: getEnvVar('LOG_FILE_PATH', './logs/middleware.log'), maxFiles: getEnvNumber('LOG_MAX_FILES', 5), maxSize: getEnvVar('LOG_MAX_SIZE', '10m') }, cors: { origin: getEnvVar('CORS_ORIGIN', '*') }, database: { mainUri: getEnvVar('MAIN_DB_URI', '', false), // Optional for npm package mcpUri: getEnvVar('MCP_DB_URI', '', false), // Optional for npm package mongoUri: getEnvVar('MONGODB_URI', '', false), // Legacy support options: { maxPoolSize: getEnvNumber('DB_MAX_POOL_SIZE', 10), serverSelectionTimeoutMS: getEnvNumber('DB_SERVER_SELECTION_TIMEOUT', 5000), socketTimeoutMS: getEnvNumber('DB_SOCKET_TIMEOUT', 45000) } }, apiKeys: resolveApiKeys() }; // šŸ“Š Configuration reporting export const getConfigSummary = () => { const userKeyCount = Object.keys(config.apiKeys.user).length; const tryaiiKeyCount = Object.keys(config.apiKeys.tryaii).length; return ` šŸ—ļø TryAII MCP Server Configuration: ā”œā”€ Server: ${config.server.host}:${config.server.port} (${config.server.nodeEnv}) ā”œā”€ Modes: HTTP=${config.server.modes.http}, MCP=${config.server.modes.mcp}${config.server.modes.mcpPort ? `:${config.server.modes.mcpPort}` : ''} ā”œā”€ Session: ${config.session.timeoutMinutes}min timeout, ${config.session.maxConcurrentSessions} max concurrent ā”œā”€ User Keys: ${userKeyCount > 0 ? `${userKeyCount} configured (${Object.keys(config.apiKeys.user).join(', ')})` : 'None - will use TryAII fallbacks'} ā”œā”€ TryAII Keys: ${tryaiiKeyCount > 0 ? `${tryaiiKeyCount} available` : 'None configured'} └─ Database: Main=${config.database.mainUri.includes('localhost') ? 'Local' : 'Atlas'}, MCP=${config.database.mcpUri.includes('localhost') ? 'Local' : 'Atlas'}`; }; // Validation export const validateConfig = () => { // Check MCP executable (only required if MCP mode is enabled) if (config.server.modes.mcp && !config.mcp.executablePath) { console.warn('āš ļø MCP mode enabled but no executable path set. Disabling MCP mode.'); config.server.modes.mcp = false; } // Check database URIs (warn if missing in production, don't crash) if (process.env.NODE_ENV === 'production') { if (!config.database.mainUri) { console.warn('āš ļø MAIN_DB_URI not configured. Database features will be disabled.'); } if (!config.database.mcpUri) { console.warn('āš ļø MCP_DB_URI not configured. MCP session storage will be disabled.'); } } // Validate numeric ranges if (config.server.port < 1 || config.server.port > 65535) { throw new Error('Server port must be between 1 and 65535'); } if (config.session.timeoutMinutes < 1) { throw new Error('Session timeout must be at least 1 minute'); } // Check API key availability const totalAvailableKeys = Object.keys(config.apiKeys.user).length + Object.keys(config.apiKeys.tryaii).length; if (totalAvailableKeys === 0) { console.warn('āš ļø No API keys configured. The MCP server may not function properly.'); } // Debug: Show what we loaded console.log('šŸ”§ Configuration loaded:'); console.log(` Port: ${config.server.port}`); console.log(` MCP Mode: ${config.server.modes.mcp}`); console.log(` HTTP Mode: ${config.server.modes.http}`); console.log(` Database Main: ${config.database.mainUri ? 'Configured' : 'Missing'}`); console.log(` Database MCP: ${config.database.mcpUri ? 'Configured' : 'Missing'}`); console.log(` API Keys: ${totalAvailableKeys} total`); console.log(` Environment: ${config.server.nodeEnv}`); // Only show config summary in development if (process.env.NODE_ENV === 'development') { console.log(getConfigSummary()); } }; // Don't auto-validate - let the main application call validateConfig() when needed //# sourceMappingURL=config.js.map