Skip to main content
Glama

firefox-devtools-mcp

setup-mcp-config.js9.49 kB
#!/usr/bin/env node import fs from 'fs'; import path from 'path'; import readline from 'readline'; import os from 'os'; import { fileURLToPath } from 'url'; import { execSync } from 'child_process'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); // Colors for terminal output const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', red: '\x1b[31m', }; function question(query) { return new Promise((resolve) => rl.question(query, resolve)); } function detectNodePath() { try { // First try to get current Node.js path const nodeVersion = process.version; console.log(`${colors.green}✓ Detected Node.js version: ${nodeVersion}${colors.reset}`); // Check if using nvm const nvmDir = process.env.NVM_DIR; if (nvmDir) { // Try to get the current version from nvm try { const currentVersion = execSync('nvm current', { encoding: 'utf8' }).trim(); const nodePath = path.join(nvmDir, 'versions', 'node', currentVersion, 'bin', 'node'); if (fs.existsSync(nodePath)) { console.log(`${colors.green}✓ Using nvm Node.js at: ${nodePath}${colors.reset}`); return nodePath; } } catch (e) { // nvm not available, continue } } // Use the current Node.js executable path console.log(`${colors.green}✓ Using Node.js at: ${process.execPath}${colors.reset}`); return process.execPath; } catch (error) { console.log(`${colors.yellow}⚠️ Could not detect Node.js path, using default${colors.reset}`); return 'node'; } } async function main() { console.log( `${colors.bright}${colors.blue}🚀 Firefox DevTools MCP Configuration Setup${colors.reset}\n` ); // Ask which client to configure console.log(`${colors.bright}Select MCP client to configure:${colors.reset}`); console.log('1. Claude Desktop (desktop app)'); console.log('2. Claude Code (CLI tool)'); console.log('3. Both'); console.log('4. Display config only (manual setup)'); const clientChoice = (await question('\nSelect option (1-4) [2]: ')) || '2'; // Get project path const projectPath = path.resolve(__dirname, '..'); const distIndexPath = path.join(projectPath, 'dist', 'index.js'); // Check if we need to build first const srcIndexPath = path.join(projectPath, 'src', 'index.ts'); if (!fs.existsSync(distIndexPath) && fs.existsSync(srcIndexPath)) { console.log(`${colors.yellow}⚠️ Dist file not found. Building project...${colors.reset}`); try { execSync('npm run build', { cwd: projectPath, stdio: 'inherit' }); console.log(`${colors.green}✓ Build completed${colors.reset}\n`); } catch (error) { console.log( `${colors.red}❌ Build failed. Please run 'npm run build' manually${colors.reset}` ); process.exit(1); } } if (!fs.existsSync(distIndexPath)) { console.log(`${colors.red}❌ Dist file not found at: ${distIndexPath}${colors.reset}`); console.log(`${colors.yellow}Please run 'npm run build' first${colors.reset}`); process.exit(1); } console.log(`${colors.green}✓ Found server dist at: ${distIndexPath}${colors.reset}\n`); // Get Firefox configuration console.log(`${colors.bright}Firefox Configuration:${colors.reset}`); console.log(`${colors.yellow}(Press Enter to use default values)${colors.reset}\n`); const headless = (await question('Run in headless mode? (y/n) [n]: ')) || 'n'; const viewport = (await question('Viewport size (e.g., 1280x720) [1280x720]: ')) || '1280x720'; // Detect Node.js path const nodePath = detectNodePath(); // Ask user about Node.js configuration console.log(`\n${colors.bright}Node.js Configuration:${colors.reset}`); console.log(`1. Use detected Node.js: ${nodePath}`); console.log(`2. Specify custom Node.js path`); console.log(`3. Use system default (node)`); const nodeChoice = await question('\nSelect option (1-3) [1]: ') || '1'; let finalNodePath = nodePath; switch (nodeChoice) { case '2': finalNodePath = await question('Enter full path to Node.js executable: '); if (!fs.existsSync(finalNodePath)) { console.log(`${colors.yellow}⚠️ Path not found, using detected path${colors.reset}`); finalNodePath = nodePath; } break; case '3': finalNodePath = 'node'; break; } // Create MCP config const mcpConfig = { mcpServers: { 'firefox-devtools': { command: finalNodePath, args: [distIndexPath, '--headless=' + (headless.toLowerCase() === 'y' ? 'true' : 'false'), '--viewport=' + viewport], }, }, }; // Determine config paths based on client choice const platform = os.platform(); let desktopConfigPath, desktopConfigDir; let claudeCodeConfigPath, claudeCodeConfigDir; // Claude Desktop paths if (platform === 'darwin') { desktopConfigDir = path.join(os.homedir(), 'Library', 'Application Support', 'Claude'); desktopConfigPath = path.join(desktopConfigDir, 'claude_desktop_config.json'); } else if (platform === 'win32') { desktopConfigDir = path.join(os.homedir(), 'AppData', 'Roaming', 'Claude'); desktopConfigPath = path.join(desktopConfigDir, 'claude_desktop_config.json'); } else { desktopConfigDir = path.join(os.homedir(), '.config', 'claude'); desktopConfigPath = path.join(desktopConfigDir, 'claude_desktop_config.json'); } // Claude Code paths if (platform === 'darwin') { claudeCodeConfigDir = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'Code'); claudeCodeConfigPath = path.join(claudeCodeConfigDir, 'mcp_settings.json'); } else if (platform === 'win32') { claudeCodeConfigDir = path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'Code'); claudeCodeConfigPath = path.join(claudeCodeConfigDir, 'mcp_settings.json'); } else { claudeCodeConfigDir = path.join(os.homedir(), '.config', 'claude', 'code'); claudeCodeConfigPath = path.join(claudeCodeConfigDir, 'mcp_settings.json'); } // Handle display-only option if (clientChoice === '4') { console.log( `\n${colors.bright}Copy this configuration to your MCP config:${colors.reset}\n` ); console.log(JSON.stringify(mcpConfig, null, 2)); console.log(`\n${colors.bright}${colors.blue}Config file locations:${colors.reset}`); console.log(`Claude Desktop: ${desktopConfigPath}`); console.log(`Claude Code: ${claudeCodeConfigPath}`); rl.close(); return; } // Helper function to save config async function saveConfig(configPath, configDir, clientName) { let existingConfig = {}; if (fs.existsSync(configPath)) { try { existingConfig = JSON.parse(fs.readFileSync(configPath, 'utf8')); if (existingConfig.mcpServers && existingConfig.mcpServers['firefox-devtools']) { const overwrite = await question( `\n${colors.yellow}⚠️ 'firefox-devtools' server already exists in ${clientName}. Overwrite? (y/n): ${colors.reset}` ); if (overwrite.toLowerCase() !== 'y') { console.log(`Skipped ${clientName}`); return false; } } } catch (error) { console.log( `${colors.yellow}⚠️ Could not read existing ${clientName} config, will create new one${colors.reset}` ); } } // Merge configs const finalConfig = { ...existingConfig, mcpServers: { ...existingConfig.mcpServers, ...mcpConfig.mcpServers, }, }; if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } fs.writeFileSync(configPath, JSON.stringify(finalConfig, null, 2)); console.log(`\n${colors.green}✅ Configuration saved to ${clientName}: ${configPath}${colors.reset}`); return true; } // Save to appropriate client(s) if (clientChoice === '1') { await saveConfig(desktopConfigPath, desktopConfigDir, 'Claude Desktop'); console.log(`${colors.yellow}⚠️ Restart Claude Desktop to apply changes${colors.reset}`); } else if (clientChoice === '2') { await saveConfig(claudeCodeConfigPath, claudeCodeConfigDir, 'Claude Code'); console.log(`${colors.yellow}⚠️ Restart Claude Code to apply changes${colors.reset}`); } else if (clientChoice === '3') { await saveConfig(desktopConfigPath, desktopConfigDir, 'Claude Desktop'); await saveConfig(claudeCodeConfigPath, claudeCodeConfigDir, 'Claude Code'); console.log(`${colors.yellow}⚠️ Restart Claude Desktop and Claude Code to apply changes${colors.reset}`); } // Show next steps console.log(`\n${colors.bright}${colors.blue}Next Steps:${colors.reset}`); if (clientChoice === '1' || clientChoice === '3') { console.log(`1. Restart Claude Desktop`); } if (clientChoice === '2' || clientChoice === '3') { console.log(`${clientChoice === '3' ? '2' : '1'}. Restart Claude Code (or reload window)`); } console.log( `${clientChoice === '3' ? '3' : '2'}. Test with: "${colors.green}List all open pages in Firefox${colors.reset}" or "${colors.green}Take a screenshot${colors.reset}"` ); rl.close(); } main().catch((error) => { console.error(`${colors.red}Error: ${error.message}${colors.reset}`); process.exit(1); });

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/freema/firefox-devtools-mcp'

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