Skip to main content
Glama
config-manager.js11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TelemetryConfigManager = void 0; const fs_1 = require("fs"); const path_1 = require("path"); const os_1 = require("os"); const crypto_1 = require("crypto"); const os_2 = require("os"); class TelemetryConfigManager { constructor() { this.config = null; this.configDir = (0, path_1.join)((0, os_1.homedir)(), '.n8n-mcp'); this.configPath = (0, path_1.join)(this.configDir, 'telemetry.json'); } static getInstance() { if (!TelemetryConfigManager.instance) { TelemetryConfigManager.instance = new TelemetryConfigManager(); } return TelemetryConfigManager.instance; } generateUserId() { if (process.env.IS_DOCKER === 'true' || this.isCloudEnvironment()) { return this.generateDockerStableId(); } const machineId = `${(0, os_2.hostname)()}-${(0, os_2.platform)()}-${(0, os_2.arch)()}-${(0, os_1.homedir)()}`; return (0, crypto_1.createHash)('sha256').update(machineId).digest('hex').substring(0, 16); } generateDockerStableId() { const bootId = this.readBootId(); if (bootId) { const fingerprint = `${bootId}-${(0, os_2.platform)()}-${(0, os_2.arch)()}`; return (0, crypto_1.createHash)('sha256').update(fingerprint).digest('hex').substring(0, 16); } const combinedFingerprint = this.generateCombinedFingerprint(); if (combinedFingerprint) { return combinedFingerprint; } const genericId = `docker-${(0, os_2.platform)()}-${(0, os_2.arch)()}`; return (0, crypto_1.createHash)('sha256').update(genericId).digest('hex').substring(0, 16); } readBootId() { try { const bootIdPath = '/proc/sys/kernel/random/boot_id'; if (!(0, fs_1.existsSync)(bootIdPath)) { return null; } const bootId = (0, fs_1.readFileSync)(bootIdPath, 'utf-8').trim(); const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; if (!uuidRegex.test(bootId)) { return null; } return bootId; } catch (error) { return null; } } generateCombinedFingerprint() { try { const signals = []; if ((0, fs_1.existsSync)('/proc/cpuinfo')) { const cpuinfo = (0, fs_1.readFileSync)('/proc/cpuinfo', 'utf-8'); const cores = (cpuinfo.match(/processor\s*:/g) || []).length; if (cores > 0) { signals.push(`cores:${cores}`); } } if ((0, fs_1.existsSync)('/proc/meminfo')) { const meminfo = (0, fs_1.readFileSync)('/proc/meminfo', 'utf-8'); const totalMatch = meminfo.match(/MemTotal:\s+(\d+)/); if (totalMatch) { signals.push(`mem:${totalMatch[1]}`); } } if ((0, fs_1.existsSync)('/proc/version')) { const version = (0, fs_1.readFileSync)('/proc/version', 'utf-8'); const kernelMatch = version.match(/Linux version ([\d.]+)/); if (kernelMatch) { signals.push(`kernel:${kernelMatch[1]}`); } } signals.push((0, os_2.platform)(), (0, os_2.arch)()); if (signals.length < 3) { return null; } const fingerprint = signals.join('-'); return (0, crypto_1.createHash)('sha256').update(fingerprint).digest('hex').substring(0, 16); } catch (error) { return null; } } isCloudEnvironment() { return !!(process.env.RAILWAY_ENVIRONMENT || process.env.RENDER || process.env.FLY_APP_NAME || process.env.HEROKU_APP_NAME || process.env.AWS_EXECUTION_ENV || process.env.KUBERNETES_SERVICE_HOST || process.env.GOOGLE_CLOUD_PROJECT || process.env.AZURE_FUNCTIONS_ENVIRONMENT); } loadConfig() { if (this.config) { return this.config; } if (!(0, fs_1.existsSync)(this.configPath)) { const version = this.getPackageVersion(); const envDisabled = this.isDisabledByEnvironment(); this.config = { enabled: !envDisabled, userId: this.generateUserId(), firstRun: new Date().toISOString(), version }; this.saveConfig(); if (!envDisabled) { this.showFirstRunNotice(); } return this.config; } try { const rawConfig = (0, fs_1.readFileSync)(this.configPath, 'utf-8'); this.config = JSON.parse(rawConfig); if (!this.config.userId) { this.config.userId = this.generateUserId(); this.saveConfig(); } return this.config; } catch (error) { console.error('Failed to load telemetry config, using defaults:', error); this.config = { enabled: false, userId: this.generateUserId() }; return this.config; } } saveConfig() { if (!this.config) return; try { if (!(0, fs_1.existsSync)(this.configDir)) { (0, fs_1.mkdirSync)(this.configDir, { recursive: true }); } this.config.lastModified = new Date().toISOString(); (0, fs_1.writeFileSync)(this.configPath, JSON.stringify(this.config, null, 2)); } catch (error) { console.error('Failed to save telemetry config:', error); } } isEnabled() { if (this.isDisabledByEnvironment()) { return false; } const config = this.loadConfig(); return config.enabled; } isDisabledByEnvironment() { const envVars = [ 'N8N_MCP_TELEMETRY_DISABLED', 'TELEMETRY_DISABLED', 'DISABLE_TELEMETRY' ]; for (const varName of envVars) { const value = process.env[varName]; if (value !== undefined) { const normalized = value.toLowerCase().trim(); if (!['true', 'false', '1', '0', ''].includes(normalized)) { console.warn(`⚠️ Invalid telemetry environment variable value: ${varName}="${value}"\n` + ` Use "true" to disable or "false" to enable telemetry.`); } if (normalized === 'true' || normalized === '1') { return true; } } } return false; } getUserId() { const config = this.loadConfig(); return config.userId; } isFirstRun() { return !(0, fs_1.existsSync)(this.configPath); } enable() { const config = this.loadConfig(); config.enabled = true; this.config = config; this.saveConfig(); console.log('✓ Anonymous telemetry enabled'); } disable() { const config = this.loadConfig(); config.enabled = false; this.config = config; this.saveConfig(); console.log('✓ Anonymous telemetry disabled'); } getStatus() { const config = this.loadConfig(); const envDisabled = this.isDisabledByEnvironment(); let status = config.enabled ? 'ENABLED' : 'DISABLED'; if (envDisabled) { status = 'DISABLED (via environment variable)'; } return ` Telemetry Status: ${status} Anonymous ID: ${config.userId} First Run: ${config.firstRun || 'Unknown'} Config Path: ${this.configPath} To opt-out: npx n8n-mcp telemetry disable To opt-in: npx n8n-mcp telemetry enable For Docker: Set N8N_MCP_TELEMETRY_DISABLED=true `; } showFirstRunNotice() { console.log(` ╔════════════════════════════════════════════════════════════╗ ║ Anonymous Usage Statistics ║ ╠════════════════════════════════════════════════════════════╣ ║ ║ ║ n8n-mcp collects anonymous usage data to improve the ║ ║ tool and understand how it's being used. ║ ║ ║ ║ We track: ║ ║ • Which MCP tools are used (no parameters) ║ ║ • Workflow structures (sanitized, no sensitive data) ║ ║ • Error patterns (hashed, no details) ║ ║ • Performance metrics (timing, success rates) ║ ║ ║ ║ We NEVER collect: ║ ║ • URLs, API keys, or credentials ║ ║ • Workflow content or actual data ║ ║ • Personal or identifiable information ║ ║ • n8n instance details or locations ║ ║ ║ ║ Your anonymous ID: ${this.config?.userId || 'generating...'} ║ ║ ║ ║ This helps me understand usage patterns and improve ║ ║ n8n-mcp for everyone. Thank you for your support! ║ ║ ║ ║ To opt-out at any time: ║ ║ npx n8n-mcp telemetry disable ║ ║ ║ ║ Data deletion requests: ║ ║ Email romuald@n8n-mcp.com with your anonymous ID ║ ║ ║ ║ Learn more: ║ ║ https://github.com/czlonkowski/n8n-mcp/blob/main/PRIVACY.md ║ ║ ║ ╚════════════════════════════════════════════════════════════╝ `); } getPackageVersion() { try { const possiblePaths = [ (0, path_1.resolve)(__dirname, '..', '..', 'package.json'), (0, path_1.resolve)(process.cwd(), 'package.json'), (0, path_1.resolve)(__dirname, '..', '..', '..', 'package.json') ]; for (const packagePath of possiblePaths) { if ((0, fs_1.existsSync)(packagePath)) { const packageJson = JSON.parse((0, fs_1.readFileSync)(packagePath, 'utf-8')); if (packageJson.version) { return packageJson.version; } } } try { const packageJson = require('../../package.json'); return packageJson.version || 'unknown'; } catch { } return 'unknown'; } catch (error) { return 'unknown'; } } } exports.TelemetryConfigManager = TelemetryConfigManager; //# sourceMappingURL=config-manager.js.map

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/czlonkowski/n8n-mcp'

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