Skip to main content
Glama

Gemini MCP Server

by mintmcqueen
utils.js9.93 kB
#!/usr/bin/env node import { fileURLToPath } from 'url'; import { dirname, join, resolve } from 'path'; import { homedir, platform } from 'os'; import { existsSync, readFileSync, writeFileSync, appendFileSync, chmodSync } from 'fs'; import { createInterface } from 'readline'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * Detect user's shell and appropriate config file */ export function detectShell() { const shell = process.env.SHELL || ''; const isZsh = shell.includes('zsh'); const isBash = shell.includes('bash'); const home = homedir(); const configFiles = { zsh: [ join(home, '.zshrc'), join(home, '.zshenv'), ], bash: [ join(home, '.bashrc'), join(home, '.bash_profile'), join(home, '.profile'), ], }; if (isZsh) { return { shell: 'zsh', configFile: configFiles.zsh.find(f => existsSync(f)) || configFiles.zsh[0], allConfigFiles: configFiles.zsh, }; } else if (isBash) { return { shell: 'bash', configFile: configFiles.bash.find(f => existsSync(f)) || configFiles.bash[0], allConfigFiles: configFiles.bash, }; } return { shell: 'unknown', configFile: join(home, '.profile'), allConfigFiles: [join(home, '.profile')], }; } /** * Get Claude Desktop config path based on platform */ export function getClaudeDesktopConfigPath() { const home = homedir(); const plat = platform(); switch (plat) { case 'darwin': // macOS return join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'); case 'win32': // Windows return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json'); default: // Linux and others return join(home, '.config', 'Claude', 'claude_desktop_config.json'); } } /** * Get Claude Code config path based on scope */ export function getClaudeCodeConfigPath(scope = 'user') { const home = homedir(); switch (scope) { case 'user': return join(home, '.claude.json'); case 'project': return join(process.cwd(), '.mcp.json'); case 'local': return join(process.cwd(), '.claude', 'settings.local.json'); default: return join(home, '.claude.json'); } } /** * Check if running in interactive terminal */ export function isInteractive() { return process.stdin.isTTY && process.stdout.isTTY; } /** * Check if environment variable exists in any shell config */ export function checkExistingEnvVar(varName) { const { allConfigFiles } = detectShell(); for (const file of allConfigFiles) { if (existsSync(file)) { const content = readFileSync(file, 'utf8'); const regex = new RegExp(`^export\\s+${varName}=`, 'm'); if (regex.test(content)) { return { exists: true, file }; } } } // Also check current environment if (process.env[varName]) { return { exists: true, file: 'environment' }; } return { exists: false, file: null }; } /** * Prompt user with masked input */ export function promptMasked(question) { return new Promise((resolve) => { const rl = createInterface({ input: process.stdin, output: process.stdout, }); // Mute output during input const onData = () => { process.stdout.write('*'); }; process.stdin.on('data', onData); rl.question(question, (answer) => { process.stdin.removeListener('data', onData); rl.close(); console.log(''); // New line after masked input resolve(answer.trim()); }); // Hide initial input echo rl._writeToOutput = () => {}; }); } /** * Prompt user with normal input */ export function prompt(question) { return new Promise((resolve) => { const rl = createInterface({ input: process.stdin, output: process.stdout, }); rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }); }); } /** * Write environment variable to shell config */ export function writeToShellConfig(varName, value) { const { configFile, shell } = detectShell(); try { // Check if already exists let content = ''; if (existsSync(configFile)) { content = readFileSync(configFile, 'utf8'); const regex = new RegExp(`^export\\s+${varName}=.*$`, 'gm'); if (regex.test(content)) { // Update existing entry content = content.replace(regex, `export ${varName}="${value}"`); writeFileSync(configFile, content, 'utf8'); console.log(`✓ Updated ${varName} in ${configFile}`); return { success: true, file: configFile, action: 'updated' }; } } // Append new entry const timestamp = new Date().toISOString(); const entry = `\n# Gemini API Key (added by @mintmcqueen/gemini-mcp on ${timestamp})\nexport ${varName}="${value}"\n`; appendFileSync(configFile, entry, 'utf8'); console.log(`✓ Added ${varName} to ${configFile}`); console.log(`\n⚠️ Run: source ${configFile}`); console.log(` Or restart your terminal for changes to take effect.\n`); return { success: true, file: configFile, action: 'added' }; } catch (error) { console.error(`✗ Failed to write to ${configFile}:`, error.message); return { success: false, error: error.message }; } } /** * Write or update Claude Code config (stdio MCP server) */ export function writeToClaudeCodeConfig(apiKey, scope = 'user') { const configPath = getClaudeCodeConfigPath(scope); const packageDir = resolve(__dirname, '..'); const serverPath = join(packageDir, 'build', 'index.js'); try { // Read existing config or create new let config = { mcpServers: {} }; if (existsSync(configPath)) { try { config = JSON.parse(readFileSync(configPath, 'utf8')); if (!config.mcpServers) { config.mcpServers = {}; } } catch (parseError) { console.warn('⚠️ Existing config invalid, will create new'); config = { mcpServers: {} }; } } // Add or update gemini server (stdio type for Claude Code) config.mcpServers.gemini = { type: 'stdio', command: 'node', args: [serverPath], env: { GEMINI_API_KEY: apiKey, }, }; // Write atomically const tempPath = `${configPath}.tmp`; const configDir = dirname(configPath); // Ensure directory exists import('fs').then(async fs => { await fs.promises.mkdir(configDir, { recursive: true }); // Write with pretty formatting writeFileSync(tempPath, JSON.stringify(config, null, 2), 'utf8'); // Set permissions (user read/write only) chmodSync(tempPath, 0o600); // Rename to final location fs.renameSync(tempPath, configPath); console.log(`✓ Updated Claude Code config (${scope} scope): ${configPath}`); console.log(`\n⚠️ Restart Claude Code for changes to take effect.\n`); return { success: true, file: configPath, scope }; }); return { success: true, file: configPath, scope }; } catch (error) { console.error(`✗ Failed to write Claude Code config:`, error.message); return { success: false, error: error.message }; } } /** * Write or update Claude Desktop config */ export function writeToClaudeDesktopConfig(apiKey) { const configPath = getClaudeDesktopConfigPath(); const packageDir = resolve(__dirname, '..'); const serverPath = join(packageDir, 'build', 'index.js'); try { // Read existing config or create new let config = { mcpServers: {} }; if (existsSync(configPath)) { try { config = JSON.parse(readFileSync(configPath, 'utf8')); if (!config.mcpServers) { config.mcpServers = {}; } } catch (parseError) { console.warn('⚠️ Existing config invalid, will overwrite'); config = { mcpServers: {} }; } } // Add or update gemini server (no type specified for Claude Desktop) config.mcpServers.gemini = { command: 'node', args: [serverPath], env: { GEMINI_API_KEY: apiKey, }, }; // Write atomically const tempPath = `${configPath}.tmp`; const configDir = dirname(configPath); // Ensure directory exists import('fs').then(async fs => { await fs.promises.mkdir(configDir, { recursive: true }); // Write with pretty formatting writeFileSync(tempPath, JSON.stringify(config, null, 2), 'utf8'); // Set permissions (user read/write only) chmodSync(tempPath, 0o600); // Rename to final location fs.renameSync(tempPath, configPath); console.log(`✓ Updated Claude Desktop config: ${configPath}`); console.log(`\n⚠️ Restart Claude Desktop for changes to take effect.\n`); return { success: true, file: configPath }; }); return { success: true, file: configPath }; } catch (error) { console.error(`✗ Failed to write Claude Desktop config:`, error.message); return { success: false, error: error.message }; } } /** * Get package installation directory */ export function getPackageDir() { return resolve(__dirname, '..'); } /** * Colors for terminal output */ export const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m', }; /** * Format success message */ export function success(msg) { console.log(`${colors.green}✓${colors.reset} ${msg}`); } /** * Format error message */ export function error(msg) { console.log(`${colors.red}✗${colors.reset} ${msg}`); } /** * Format warning message */ export function warn(msg) { console.log(`${colors.yellow}⚠${colors.reset} ${msg}`); } /** * Format info message */ export function info(msg) { console.log(`${colors.cyan}ℹ${colors.reset} ${msg}`); }

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/mintmcqueen/gemini-mcp'

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