Skip to main content
Glama

n8n-MCP

by 88-888
parse-config.jsβ€’5.79 kB
#!/usr/bin/env node /** * Parse JSON config file and output shell-safe export commands * Only outputs variables that aren't already set in environment * * Security: Uses safe quoting without any shell execution */ const fs = require('fs'); // Debug logging support const DEBUG = process.env.DEBUG_CONFIG === 'true'; function debugLog(message) { if (DEBUG) { process.stderr.write(`[parse-config] ${message}\n`); } } const configPath = process.argv[2] || '/app/config.json'; debugLog(`Using config path: ${configPath}`); // Dangerous environment variables that should never be set const DANGEROUS_VARS = new Set([ 'PATH', 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'LD_AUDIT', 'BASH_ENV', 'ENV', 'CDPATH', 'IFS', 'PS1', 'PS2', 'PS3', 'PS4', 'SHELL', 'BASH_FUNC', 'SHELLOPTS', 'GLOBIGNORE', 'PERL5LIB', 'PYTHONPATH', 'NODE_PATH', 'RUBYLIB' ]); /** * Sanitize a key name for use as environment variable * Converts to uppercase and replaces invalid chars with underscore */ function sanitizeKey(key) { // Convert to string and handle edge cases const keyStr = String(key || '').trim(); if (!keyStr) { return 'EMPTY_KEY'; } // Special handling for NODE_DB_PATH to preserve exact casing if (keyStr === 'NODE_DB_PATH') { return 'NODE_DB_PATH'; } const sanitized = keyStr .toUpperCase() .replace(/[^A-Z0-9]+/g, '_') .replace(/^_+|_+$/g, '') // Trim underscores .replace(/^(\d)/, '_$1'); // Prefix with _ if starts with number // If sanitization results in empty string, use a default return sanitized || 'EMPTY_KEY'; } /** * Safely quote a string for shell use * This follows POSIX shell quoting rules */ function shellQuote(str) { // Remove null bytes which are not allowed in environment variables str = str.replace(/\x00/g, ''); // Always use single quotes for consistency and safety // Single quotes protect everything except other single quotes return "'" + str.replace(/'/g, "'\"'\"'") + "'"; } try { if (!fs.existsSync(configPath)) { debugLog(`Config file not found at: ${configPath}`); process.exit(0); // Silent exit if no config file } let configContent; let config; try { configContent = fs.readFileSync(configPath, 'utf8'); debugLog(`Read config file, size: ${configContent.length} bytes`); } catch (readError) { // Silent exit on read errors debugLog(`Error reading config: ${readError.message}`); process.exit(0); } try { config = JSON.parse(configContent); debugLog(`Parsed config with ${Object.keys(config).length} top-level keys`); } catch (parseError) { // Silent exit on invalid JSON debugLog(`Error parsing JSON: ${parseError.message}`); process.exit(0); } // Validate config is an object if (typeof config !== 'object' || config === null || Array.isArray(config)) { // Silent exit on invalid config structure process.exit(0); } // Convert nested objects to flat environment variables const flattenConfig = (obj, prefix = '', depth = 0) => { const result = {}; // Prevent infinite recursion if (depth > 10) { return result; } for (const [key, value] of Object.entries(obj)) { const sanitizedKey = sanitizeKey(key); // Skip if sanitization resulted in EMPTY_KEY (indicating invalid key) if (sanitizedKey === 'EMPTY_KEY') { debugLog(`Skipping key '${key}': invalid key name`); continue; } const envKey = prefix ? `${prefix}_${sanitizedKey}` : sanitizedKey; // Skip if key is too long if (envKey.length > 255) { debugLog(`Skipping key '${envKey}': too long (${envKey.length} chars)`); continue; } if (typeof value === 'object' && value !== null && !Array.isArray(value)) { // Recursively flatten nested objects Object.assign(result, flattenConfig(value, envKey, depth + 1)); } else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { // Only include if not already set in environment if (!process.env[envKey]) { let stringValue = String(value); // Handle special JavaScript number values if (typeof value === 'number') { if (!isFinite(value)) { if (value === Infinity) { stringValue = 'Infinity'; } else if (value === -Infinity) { stringValue = '-Infinity'; } else if (isNaN(value)) { stringValue = 'NaN'; } } } // Skip if value is too long if (stringValue.length <= 32768) { result[envKey] = stringValue; } } } } return result; }; // Output shell-safe export commands const flattened = flattenConfig(config); const exports = []; for (const [key, value] of Object.entries(flattened)) { // Validate key name (alphanumeric and underscore only) if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) { continue; // Skip invalid variable names } // Skip dangerous variables if (DANGEROUS_VARS.has(key) || key.startsWith('BASH_FUNC_')) { debugLog(`Warning: Ignoring dangerous variable: ${key}`); process.stderr.write(`Warning: Ignoring dangerous variable: ${key}\n`); continue; } // Safely quote the value const quotedValue = shellQuote(value); exports.push(`export ${key}=${quotedValue}`); } // Use process.stdout.write to ensure output goes to stdout if (exports.length > 0) { process.stdout.write(exports.join('\n') + '\n'); } } catch (error) { // Silent fail - don't break the container startup process.exit(0); }

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

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