UNPKG

twenty-mcp-server

Version:

Easy-to-install Model Context Protocol server for Twenty CRM. Try instantly with 'npx twenty-mcp-server setup' or install globally for permanent use.

283 lines 10.2 kB
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { join } from 'path'; import chalk from 'chalk'; import { getPlatformConfigDir } from './platform-utils.js'; export class ConfigManager { configDir; configPath; envPath; constructor(projectPath) { if (projectPath) { // Local project configuration this.configDir = projectPath; this.configPath = join(projectPath, '.twenty-mcp.json'); this.envPath = join(projectPath, '.env'); } else { // Global user configuration - platform-specific const platformConfigDir = getPlatformConfigDir(); this.configDir = join(platformConfigDir, 'twenty-mcp'); this.configPath = join(this.configDir, 'config.json'); this.envPath = join(this.configDir, '.env'); } } ensureConfigDir() { if (!existsSync(this.configDir)) { mkdirSync(this.configDir, { recursive: true }); } } getDefaultConfig() { return { version: '1.1.0', installation: { installPath: process.cwd(), installedAt: new Date().toISOString(), }, twenty: {}, auth: { enabled: false, requireAuth: false, }, ipProtection: { enabled: false, allowlist: ['127.0.0.1'], trustedProxies: [], blockUnknownIPs: true, }, server: { port: 3000, mode: 'http', verbose: false, }, preferences: { autoStart: false, checkUpdates: true, telemetry: false, }, }; } load() { if (!existsSync(this.configPath)) { return this.getDefaultConfig(); } try { const configData = readFileSync(this.configPath, 'utf8'); const config = JSON.parse(configData); // Merge with defaults to ensure all fields exist const defaultConfig = this.getDefaultConfig(); return this.mergeConfigs(defaultConfig, config); } catch (error) { console.warn(chalk.yellow('⚠️ Invalid config file, using defaults')); return this.getDefaultConfig(); } } mergeConfigs(defaultConfig, userConfig) { return { version: userConfig.version || defaultConfig.version, installation: { ...defaultConfig.installation, ...userConfig.installation, }, twenty: { ...defaultConfig.twenty, ...userConfig.twenty, }, auth: { ...defaultConfig.auth, ...userConfig.auth, }, ipProtection: { ...defaultConfig.ipProtection, ...userConfig.ipProtection, }, server: { ...defaultConfig.server, ...userConfig.server, }, preferences: { ...defaultConfig.preferences, ...userConfig.preferences, }, }; } save(config) { this.ensureConfigDir(); // Update last updated timestamp config.installation.lastUpdated = new Date().toISOString(); try { const configData = JSON.stringify(config, null, 2); writeFileSync(this.configPath, configData, 'utf8'); } catch (error) { throw new Error(`Failed to save config: ${error instanceof Error ? error.message : error}`); } } updateTwentyConfig(apiKey, baseUrl) { const config = this.load(); config.twenty.apiKey = apiKey; config.twenty.baseUrl = baseUrl; this.save(config); this.syncToEnv(config); } updateAuthConfig(authConfig) { const config = this.load(); config.auth = { ...config.auth, ...authConfig }; this.save(config); this.syncToEnv(config); } updateIPProtectionConfig(ipConfig) { const config = this.load(); config.ipProtection = { ...config.ipProtection, ...ipConfig }; this.save(config); this.syncToEnv(config); } updateServerConfig(serverConfig) { const config = this.load(); config.server = { ...config.server, ...serverConfig }; this.save(config); } updatePreferences(preferences) { const config = this.load(); config.preferences = { ...config.preferences, ...preferences }; this.save(config); } // Sync configuration to .env file for backward compatibility syncToEnv(config) { const configData = config || this.load(); const envVars = new Map(); // Twenty CRM configuration if (configData.twenty.apiKey) { envVars.set('TWENTY_API_KEY', configData.twenty.apiKey); } if (configData.twenty.baseUrl) { envVars.set('TWENTY_BASE_URL', configData.twenty.baseUrl); } // Auth configuration envVars.set('AUTH_ENABLED', configData.auth.enabled.toString()); envVars.set('REQUIRE_AUTH', configData.auth.requireAuth.toString()); if (configData.auth.provider) { envVars.set('AUTH_PROVIDER', configData.auth.provider); } if (configData.auth.clerkPublishableKey) { envVars.set('CLERK_PUBLISHABLE_KEY', configData.auth.clerkPublishableKey); } if (configData.auth.clerkSecretKey) { envVars.set('CLERK_SECRET_KEY', configData.auth.clerkSecretKey); } if (configData.auth.clerkDomain) { envVars.set('CLERK_DOMAIN', configData.auth.clerkDomain); } if (configData.auth.encryptionSecret) { envVars.set('API_KEY_ENCRYPTION_SECRET', configData.auth.encryptionSecret); } // IP Protection configuration envVars.set('IP_PROTECTION_ENABLED', configData.ipProtection.enabled.toString()); if (configData.ipProtection.allowlist.length > 0) { envVars.set('IP_ALLOWLIST', configData.ipProtection.allowlist.join(',')); } if (configData.ipProtection.trustedProxies.length > 0) { envVars.set('TRUSTED_PROXIES', configData.ipProtection.trustedProxies.join(',')); } envVars.set('IP_BLOCK_UNKNOWN', configData.ipProtection.blockUnknownIPs.toString()); // Server configuration envVars.set('MCP_SERVER_URL', `http://localhost:${configData.server.port}`); // Write to .env file let envContent = ''; envVars.forEach((value, key) => { envContent += `${key}=${value}\n`; }); try { writeFileSync(this.envPath, envContent, 'utf8'); } catch (error) { console.warn(chalk.yellow('⚠️ Could not write .env file')); } } // Load environment variables into current process loadEnv() { if (!existsSync(this.envPath)) { return; } try { const envContent = readFileSync(this.envPath, 'utf8'); envContent.split('\n').forEach(line => { const match = line.match(/^([A-Z_]+)=(.*)$/); if (match) { process.env[match[1]] = match[2]; } }); } catch (error) { console.warn(chalk.yellow('⚠️ Could not load .env file')); } } getConfigPath() { return this.configPath; } getEnvPath() { return this.envPath; } exists() { return existsSync(this.configPath); } reset() { const defaultConfig = this.getDefaultConfig(); this.save(defaultConfig); this.syncToEnv(defaultConfig); } // Import from existing .env file importFromEnv() { const config = this.getDefaultConfig(); if (!existsSync(this.envPath)) { return config; } try { const envContent = readFileSync(this.envPath, 'utf8'); const envVars = new Map(); envContent.split('\n').forEach(line => { const match = line.match(/^([A-Z_]+)=(.*)$/); if (match) { envVars.set(match[1], match[2]); } }); // Import Twenty configuration if (envVars.has('TWENTY_API_KEY')) { config.twenty.apiKey = envVars.get('TWENTY_API_KEY'); } if (envVars.has('TWENTY_BASE_URL')) { config.twenty.baseUrl = envVars.get('TWENTY_BASE_URL'); } // Import auth configuration if (envVars.has('AUTH_ENABLED')) { config.auth.enabled = envVars.get('AUTH_ENABLED') === 'true'; } if (envVars.has('REQUIRE_AUTH')) { config.auth.requireAuth = envVars.get('REQUIRE_AUTH') === 'true'; } if (envVars.has('CLERK_PUBLISHABLE_KEY')) { config.auth.clerkPublishableKey = envVars.get('CLERK_PUBLISHABLE_KEY'); } if (envVars.has('CLERK_SECRET_KEY')) { config.auth.clerkSecretKey = envVars.get('CLERK_SECRET_KEY'); } // Import IP protection configuration if (envVars.has('IP_PROTECTION_ENABLED')) { config.ipProtection.enabled = envVars.get('IP_PROTECTION_ENABLED') === 'true'; } if (envVars.has('IP_ALLOWLIST')) { const allowlist = envVars.get('IP_ALLOWLIST'); if (allowlist) { config.ipProtection.allowlist = allowlist.split(',').map(ip => ip.trim()); } } this.save(config); return config; } catch (error) { console.warn(chalk.yellow('⚠️ Could not import from .env file')); return config; } } } //# sourceMappingURL=config-manager.js.map