Skip to main content
Glama

McFlow

helper.ts•11.6 kB
/** * Credential Helper for n8n * * IMPORTANT SECURITY NOTES: * - This helper NEVER logs or exposes credential values * - It only checks for credential existence * - It provides guidance for manual credential setup * - Actual credentials should be managed through n8n's UI for security */ import fs from 'fs/promises'; import path from 'path'; import * as dotenv from 'dotenv'; interface CredentialRequirement { envVar: string; n8nType: string; displayName: string; instructions: string; } export class CredentialHelper { private workflowsPath: string; constructor(workflowsPath: string) { this.workflowsPath = workflowsPath; } /** * Analyze workflows to determine required credentials */ async analyzeCredentialRequirements(): Promise<any> { try { const flowsDir = path.join(this.workflowsPath, 'flows'); const files = await fs.readdir(flowsDir); const requirements = new Map<string, CredentialRequirement>(); const workflowCredentials = new Map<string, string[]>(); // Analyze each workflow for (const file of files) { if (!file.endsWith('.json') || file.includes('package.json')) continue; const content = await fs.readFile(path.join(flowsDir, file), 'utf-8'); const workflow = JSON.parse(content); const workflowName = file.replace('.json', ''); const credTypes: string[] = []; if (workflow.nodes) { for (const node of workflow.nodes) { // Check for credential requirements if (node.credentials) { Object.keys(node.credentials).forEach(credType => { credTypes.push(credType); // Map credential types to environment variables switch (credType) { case 'openAiApi': requirements.set('OPENAI_API_KEY', { envVar: 'OPENAI_API_KEY', n8nType: 'OpenAI API', displayName: 'OpenAI API Key', instructions: '1. Go to https://platform.openai.com/api-keys\n2. Create a new API key\n3. Add to .env: OPENAI_API_KEY=sk-...', }); break; case 'replicateApi': requirements.set('REPLICATE_API_TOKEN', { envVar: 'REPLICATE_API_TOKEN', n8nType: 'Replicate API', displayName: 'Replicate API Token', instructions: '1. Go to https://replicate.com/account/api-tokens\n2. Create a new token\n3. Add to .env: REPLICATE_API_TOKEN=...', }); break; case 'airtableApi': requirements.set('AIRTABLE_API_KEY', { envVar: 'AIRTABLE_API_KEY', n8nType: 'Airtable API', displayName: 'Airtable API Key or Personal Access Token', instructions: '1. Go to https://airtable.com/create/tokens\n2. Create a personal access token\n3. Add to .env: AIRTABLE_API_KEY=pat...', }); break; case 'slackApi': requirements.set('SLACK_WEBHOOK_URL', { envVar: 'SLACK_WEBHOOK_URL', n8nType: 'Slack Webhook', displayName: 'Slack Webhook URL', instructions: '1. Go to https://api.slack.com/apps\n2. Create an app and add Incoming Webhook\n3. Add to .env: SLACK_WEBHOOK_URL=https://hooks.slack.com/...', }); break; case 'githubApi': requirements.set('GITHUB_TOKEN', { envVar: 'GITHUB_TOKEN', n8nType: 'GitHub API', displayName: 'GitHub Personal Access Token', instructions: '1. Go to https://github.com/settings/tokens\n2. Generate new token (classic)\n3. Add to .env: GITHUB_TOKEN=ghp_...', }); break; } }); } } } if (credTypes.length > 0) { workflowCredentials.set(workflowName, credTypes); } } // Check which credentials exist in .env const envVars = await this.checkEnvFile(); // Format output let output = 'šŸ” Credential Requirements Analysis\n\n'; if (requirements.size === 0) { output += 'āœ… No credentials required for these workflows.\n'; } else { output += `šŸ“‹ Required Credentials (${requirements.size}):\n\n`; for (const [envVar, req] of requirements) { const hasEnv = envVars.has(envVar); const status = hasEnv ? 'āœ…' : 'āŒ'; output += `${status} ${req.displayName}\n`; output += ` Environment Variable: ${envVar}\n`; output += ` n8n Credential Type: ${req.n8nType}\n`; if (!hasEnv) { output += ` āš ļø Not found in .env\n`; } output += '\n'; } // Show which workflows need which credentials output += 'šŸ“Š Workflows and Their Credential Requirements:\n\n'; for (const [workflow, creds] of workflowCredentials) { output += `• ${workflow}: ${creds.join(', ')}\n`; } // Instructions for missing credentials const missing = Array.from(requirements.entries()).filter(([env]) => !envVars.has(env)); if (missing.length > 0) { output += '\nāš ļø Missing Credentials Setup Instructions:\n\n'; for (const [, req] of missing) { output += `### ${req.displayName}\n${req.instructions}\n\n`; } output += 'šŸ”’ Security Best Practices:\n'; output += '1. Never commit .env files to git\n'; output += '2. Use strong, unique API keys\n'; output += '3. Rotate keys regularly\n'; output += '4. Limit API key permissions to minimum required\n'; output += '5. Add credentials through n8n UI at http://localhost:5678\n'; } } return { content: [ { type: 'text', text: output, }, ], }; } catch (error: any) { throw new Error(`Failed to analyze credentials: ${error.message}`); } } /** * Check which environment variables exist (NOT their values) */ private async checkEnvFile(): Promise<Set<string>> { const envVars = new Set<string>(); const envPaths = [ path.join(this.workflowsPath, '.env'), path.join(this.workflowsPath, '.env'), ]; for (const envPath of envPaths) { try { const envContent = await fs.readFile(envPath, 'utf-8'); const parsed = dotenv.parse(envContent); // Only store variable names, NEVER values Object.keys(parsed).forEach(key => { if (parsed[key] && parsed[key].length > 0) { envVars.add(key); } }); break; // Use first .env found } catch { // Try next path } } return envVars; } /** * Generate secure .env.example without exposing any real values */ async generateSecureEnvExample(): Promise<any> { try { const requirements = await this.getRequirementsFromWorkflows(); let content = '# n8n Workflow Credentials\n'; content += '# SECURITY: Never commit this file with real values to git!\n'; content += '# Copy to .env and add your actual API keys\n\n'; content += '# === Required API Credentials ===\n'; content += '# Get these from the respective service providers\n\n'; for (const req of requirements) { content += `# ${req.displayName}\n`; content += `# ${req.instructions.split('\n')[0]}\n`; // First line of instructions content += `${req.envVar}=\n\n`; } content += '# === Security Reminders ===\n'; content += '# 1. Add .env to .gitignore\n'; content += '# 2. Never share or log these values\n'; content += '# 3. Rotate keys regularly\n'; content += '# 4. Use environment-specific keys (dev/prod)\n'; const envExamplePath = path.join(this.workflowsPath, '.env.example'); await fs.writeFile(envExamplePath, content, 'utf-8'); return { content: [ { type: 'text', text: 'āœ… Generated secure .env.example\n\n' + 'šŸ“ Location: workflows/.env.example\n\n' + 'Next steps:\n' + '1. Copy .env.example to .env\n' + '2. Add your API keys to .env\n' + '3. Never commit .env to git\n' + '4. Add credentials in n8n UI at http://localhost:5678', }, ], }; } catch (error: any) { throw new Error(`Failed to generate .env.example: ${error.message}`); } } /** * Get credential requirements from workflows */ private async getRequirementsFromWorkflows(): Promise<CredentialRequirement[]> { // This would analyze workflows and return requirements // For now, return common ones return [ { envVar: 'OPENAI_API_KEY', n8nType: 'OpenAI API', displayName: 'OpenAI API Key', instructions: 'Get from https://platform.openai.com/api-keys', }, // Add more as needed based on workflow analysis ]; } /** * Provide instructions for setting up credentials securely */ async getCredentialSetupInstructions(): Promise<any> { return { content: [ { type: 'text', text: `šŸ” Secure Credential Setup for n8n 1ļøāƒ£ **Prepare Your Credentials** • Copy workflows/.env.example to workflows/.env • Add your API keys to the .env file • Never commit .env to version control 2ļøāƒ£ **Add Credentials in n8n UI** • Open http://localhost:5678 • Go to Credentials (left sidebar) • Click "Add Credential" • Select the credential type • Enter your API key/token • Save the credential 3ļøāƒ£ **Link Credentials to Workflows** • Open each workflow • Click on nodes that need credentials • Select the credential from dropdown • Save the workflow 4ļøāƒ£ **Security Best Practices** āœ… Use environment-specific credentials (dev/staging/prod) āœ… Rotate API keys regularly āœ… Use minimal required permissions āœ… Enable 2FA where available āœ… Monitor API usage for anomalies āŒ Never log or print credentials āŒ Never commit credentials to git āŒ Never share credentials in messages 5ļøāƒ£ **Troubleshooting** • If a workflow fails with "Credentials not found": - Check the credential exists in n8n - Verify the credential name matches - Ensure the credential has correct permissions • If API calls fail: - Test credentials directly with the service - Check rate limits - Verify API endpoint URLs For detailed setup per service: • OpenAI: https://platform.openai.com/docs/api-reference/authentication • GitHub: https://docs.github.com/en/authentication • Slack: https://api.slack.com/authentication • Airtable: https://airtable.com/developers/web/api/authentication`, }, ], }; } }

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/mckinleymedia/mcflow-mcp'

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