Skip to main content
Glama
setup.js8.9 kB
#!/usr/bin/env node import fs from 'fs/promises'; import path from 'path'; import os from 'os'; import readline from 'readline'; import { fileURLToPath } from 'url'; import fetch from 'node-fetch'; // Resolve __dirname for ES Modules const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Load config from within the package const LOCAL_CONFIG_PATHS_FILE = path.join(__dirname, '../mcp_config_paths.json'); const REMOTE_CONFIG_URL = 'https://mcpfinder.dev/mcp_config_paths.json'; const MCPFINDER_SERVER_ID = 'mcpfinder'; const MCPFINDER_SERVER_CONFIG = { command: "npx", args: [ "-y", "@mcpfinder/server" ] }; function getPlatform() { switch (os.platform()) { case 'win32': return 'windows'; case 'darwin': return 'macos'; case 'linux': return 'linux'; default: console.warn(`Unsupported platform: ${os.platform()}. Attempting to use linux paths.`); return 'linux'; } } function createPromptInterface() { return readline.createInterface({ input: process.stdin, output: process.stdout }); } function askQuestion(rl, query) { return new Promise(resolve => rl.question(query, resolve)); } async function loadConfigPaths() { // 1. Try fetching remote config console.log(`Attempting to fetch latest client paths from ${REMOTE_CONFIG_URL}...`); try { const response = await fetch(REMOTE_CONFIG_URL); if (response.ok) { const data = await response.json(); console.log("Successfully fetched latest configuration from the web."); return data; // Use remote data } else { console.warn(`Failed to fetch remote config (Status: ${response.status}). Falling back to local version.`); } } catch (fetchError) { console.warn(`Network error fetching remote config: ${fetchError.message}. Falling back to local version.`); } // 2. Fallback to local config console.log(`Loading bundled client paths from ${path.basename(LOCAL_CONFIG_PATHS_FILE)}...`); try { const data = await fs.readFile(LOCAL_CONFIG_PATHS_FILE, 'utf8'); return JSON.parse(data); } catch (localError) { console.error(`FATAL: Error loading local config paths from ${LOCAL_CONFIG_PATHS_FILE}:`, localError); console.error("This indicates a critical issue with the package build or installation."); process.exit(1); } } async function selectClient(rl, clients) { console.log("\nPlease select your MCP client:"); clients.forEach((client, index) => { console.log(`${index + 1}. ${client.tool}`); }); console.log(`${clients.length + 1}. Other (specify path manually)`); let choiceIndex = -1; while (choiceIndex < 0 || choiceIndex > clients.length) { const choice = await askQuestion(rl, `Enter number (1-${clients.length + 1}): `); const parsedChoice = parseInt(choice, 10); if (!isNaN(parsedChoice) && parsedChoice >= 1 && parsedChoice <= clients.length + 1) { choiceIndex = parsedChoice - 1; } else { console.log("Invalid choice. Please enter a number from the list."); } } if (choiceIndex === clients.length) { return null; // User chose 'Other' } return clients[choiceIndex]; } function resolvePath(filePath) { if (!filePath) return null; let resolved = filePath; // Resolve home directory if (resolved.startsWith('~')) { resolved = path.join(os.homedir(), resolved.slice(1)); } // Resolve environment variables (e.g., %USERPROFILE% on Windows) resolved = resolved.replace(/%([^%]+)%/g, (_, envVar) => { return process.env[envVar] || '' // Replace with env var value or empty string if not found }); return path.normalize(resolved); } async function askForManualPath(rl) { let filePath = ''; while (!filePath) { filePath = await askQuestion(rl, '\nPlease enter the absolute path to the MCP configuration JSON file: '); if (!filePath) { console.log("Path cannot be empty."); } } return resolvePath(filePath); } async function findConfigPath(rl, selectedClient, platform) { if (!selectedClient) { console.log("Client not specified, asking for manual path."); return await askForManualPath(rl); } const location = selectedClient.locations.find(loc => loc.operating_systems.includes(platform)); if (location && location.path) { const resolved = resolvePath(location.path); console.log(`Found default path for ${selectedClient.tool} on ${platform}: ${resolved}`); return resolved; } else { console.log(`Could not find a default configuration path for ${selectedClient.tool} on ${platform}.`); return await askForManualPath(rl); } } async function updateConfigFile(filePath, clientTool) { // Special handling for Claude Code - use claude mcp add command if (clientTool && clientTool.toLowerCase().includes('claude code')) { console.log('\n🎉 For Claude Code, use this command to add MCPfinder:'); console.log(''); console.log(' claude mcp add mcpfinder-stdio npx @mcpfinder/server'); console.log(''); console.log('Or for HTTP/SSE transport:'); console.log(''); console.log(' claude mcp add mcpfinder-http https://mcpfinder.dev/mcp'); console.log(''); console.log('Then test the connection with:'); console.log(' claude mcp test mcpfinder-stdio'); console.log(''); return; } let config = {}; let configKey = 'mcpServers'; // Default key try { const rawData = await fs.readFile(filePath, 'utf8'); if (rawData.trim()) { try { config = JSON.parse(rawData); } catch (parseError) { console.error(`Error parsing JSON from ${filePath}: ${parseError.message}`); console.log("The file seems to contain invalid JSON. Please check it manually."); process.exit(1); } } else { console.log(`Configuration file ${filePath} is empty or contains only whitespace. Initializing fresh config.`); config = {}; // Start with an empty object if the file was empty/whitespace } } catch (err) { if (err.code === 'ENOENT') { console.log(`Configuration file ${filePath} not found. Creating a new one.`); config = {}; // Initialize empty config if file doesn't exist } else { console.error(`Error reading configuration file ${filePath}:`, err); throw err; } } // Determine the correct key (mcpServers or servers) // VS Code uses 'servers' in settings.json // Check if 'servers' exists and 'mcpServers' doesn't, or if client is VS Code const isVSCode = clientTool && clientTool.toLowerCase().includes('vs code'); if ((config.hasOwnProperty('servers') && !config.hasOwnProperty('mcpServers')) || isVSCode) { configKey = 'servers'; console.log(`Using configuration key: '${configKey}'`); } else { console.log(`Using configuration key: '${configKey}'`); } // Ensure the target key exists if (!config[configKey]) { config[configKey] = {}; } // Add or update the mcpfinder entry config[configKey][MCPFINDER_SERVER_ID] = MCPFINDER_SERVER_CONFIG; console.log(`Added/Updated '${MCPFINDER_SERVER_ID}' entry under '${configKey}'.`); try { const dirPath = path.dirname(filePath); await fs.mkdir(dirPath, { recursive: true }); await fs.writeFile(filePath, JSON.stringify(config, null, 2)); console.log(`Successfully updated configuration file: ${filePath}`); } catch (err) { console.error(`Error writing configuration file ${filePath}:`, err); throw err; } } // Export the main setup logic export async function runSetup() { const configPathsData = await loadConfigPaths(); const platform = getPlatform(); const rl = createPromptInterface(); try { const selectedClient = await selectClient(rl, configPathsData); const configFilePath = await findConfigPath(rl, selectedClient, platform); if (!configFilePath) { console.error("Could not determine configuration file path. Exiting."); process.exit(1); // Exit if no path could be determined } console.log(`Using configuration file: ${configFilePath}`); await updateConfigFile(configFilePath, selectedClient ? selectedClient.tool : null); } catch (err) { console.error('An error occurred during setup:', err); process.exit(1); } finally { rl.close(); } }

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/mcpfinder/server'

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