UNPKG

@agentled/cli

Version:

CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.

201 lines 7.53 kB
/** * MCP client detection and config writing for `agentled setup`. * * Lifted from agentled-mcp-server/src/setup.ts so the CLI orchestrates the * full onboarding without depending on the mcp-server's setup flag (which * is being deprecated in favor of `agentled setup`). * * Detects Claude Code, Codex, Claude Desktop, Cursor, Windsurf and writes * the AgentLed MCP server entry into the appropriate config location. */ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { homedir } from 'node:os'; import { spawn } from 'node:child_process'; export function getClientLabel(client) { switch (client) { case 'claude-code': return 'Claude Code'; case 'claude-desktop': return 'Claude Desktop'; case 'codex': return 'Codex'; case 'cursor': return 'Cursor'; case 'windsurf': return 'Windsurf'; default: return 'Unknown'; } } export function detectMcpClient() { if (process.env.CODEX_SHELL || process.env.CODEX_THREAD_ID || process.env.CODEX_CI) return 'codex'; if (existsSync(join(homedir(), '.claude'))) return 'claude-code'; if (existsSync(join(homedir(), '.codex'))) return 'codex'; const desktop = getClaudeDesktopConfigPath(); if (desktop && existsSync(dirname(desktop))) return 'claude-desktop'; if (existsSync(join(homedir(), '.cursor'))) return 'cursor'; if (existsSync(join(homedir(), '.windsurf'))) return 'windsurf'; return 'unknown'; } function getClaudeDesktopConfigPath() { if (process.platform === 'darwin') { return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'); } if (process.platform === 'win32') { return join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json'); } return join(homedir(), '.config', 'Claude', 'claude_desktop_config.json'); } function getMcpJsonPath(client) { if (client === 'cursor') return join(homedir(), '.cursor', 'mcp.json'); if (client === 'windsurf') return join(homedir(), '.windsurf', 'mcp.json'); return null; } function configureClaudeCode(apiKey, baseUrl) { return new Promise(resolve => { try { // Server name must precede -e/--env (Claude Code 2.1+ parser quirk). const child = spawn('claude', [ 'mcp', 'add', '--transport', 'stdio', '--scope', 'user', 'agentled', '-e', `AGENTLED_API_KEY=${apiKey}`, ...(baseUrl !== 'https://www.agentled.app' ? ['-e', `AGENTLED_URL=${baseUrl}`] : []), '--', 'npx', '-y', '@agentled/mcp-server', ], { stdio: 'inherit' }); child.on('close', code => resolve(code === 0)); child.on('error', () => resolve(false)); } catch { resolve(false); } }); } function configureCodex(apiKey, baseUrl) { return new Promise(resolve => { try { const child = spawn('codex', [ 'mcp', 'add', 'agentled', '--env', `AGENTLED_API_KEY=${apiKey}`, ...(baseUrl !== 'https://www.agentled.app' ? ['--env', `AGENTLED_URL=${baseUrl}`] : []), '--', 'npx', '-y', '@agentled/mcp-server', ], { stdio: 'inherit' }); child.on('close', code => resolve(code === 0)); child.on('error', () => resolve(false)); } catch { resolve(false); } }); } function writeMcpJsonConfig(configPath, apiKey, baseUrl) { try { let config = {}; if (existsSync(configPath)) { config = JSON.parse(readFileSync(configPath, 'utf-8')); } if (!config.mcpServers) config.mcpServers = {}; const env = { AGENTLED_API_KEY: apiKey }; if (baseUrl !== 'https://www.agentled.app') env.AGENTLED_URL = baseUrl; config.mcpServers.agentled = { command: 'npx', args: ['-y', '@agentled/mcp-server'], env, }; mkdirSync(dirname(configPath), { recursive: true }); writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n'); return true; } catch { return false; } } function maskApiKey(apiKey) { if (!apiKey) return 'wsk_...'; if (apiKey.length <= 18) return `${apiKey.slice(0, 8)}...`; return `${apiKey.slice(0, 17)}...`; } export function manualSnippet(apiKey, baseUrl) { const maskedApiKey = maskApiKey(apiKey); const envArgs = baseUrl !== 'https://www.agentled.app' ? `-e AGENTLED_API_KEY=${maskedApiKey} -e AGENTLED_URL=${baseUrl}` : `-e AGENTLED_API_KEY=${maskedApiKey}`; const lines = [ 'Could not auto-detect your MCP client. Add manually:', 'Replace the masked key below with your full Agentled API key from Workspace Settings > Developer.', '', 'Claude Code:', ` claude mcp add --transport stdio --scope user agentled ${envArgs} -- npx -y @agentled/mcp-server`, '', 'Codex / Cursor / Windsurf — add to your MCP config JSON:', ' {', ' "agentled": {', ' "command": "npx",', ' "args": ["-y", "@agentled/mcp-server"],', ` "env": { "AGENTLED_API_KEY": "${maskedApiKey}"${baseUrl !== 'https://www.agentled.app' ? `, "AGENTLED_URL": "${baseUrl}"` : ''} }`, ' }', ' }', ]; return lines.join('\n'); } /** * Auto-detect the user's MCP client and register the AgentLed MCP server. * Returns a result describing what happened, including a fallback hint when * the client is unknown or the config write failed. */ export async function configureMcp(apiKey, baseUrl, target = 'auto') { const client = target === 'auto' ? detectMcpClient() : target; if (client === 'claude-code') { const ok = await configureClaudeCode(apiKey, baseUrl); return { client, success: ok, configPath: ok ? '~/.claude.json (via `claude mcp add`)' : null, fallbackHint: ok ? undefined : manualSnippet(apiKey, baseUrl), }; } if (client === 'codex') { const ok = await configureCodex(apiKey, baseUrl); return { client, success: ok, configPath: ok ? '~/.codex/config (via `codex mcp add`)' : null, fallbackHint: ok ? undefined : manualSnippet(apiKey, baseUrl), }; } if (client === 'claude-desktop') { const path = getClaudeDesktopConfigPath(); const ok = path ? writeMcpJsonConfig(path, apiKey, baseUrl) : false; return { client, success: ok, configPath: path, fallbackHint: ok ? undefined : manualSnippet(apiKey, baseUrl), }; } if (client === 'cursor' || client === 'windsurf') { const path = getMcpJsonPath(client); const ok = path ? writeMcpJsonConfig(path, apiKey, baseUrl) : false; return { client, success: ok, configPath: path, fallbackHint: ok ? undefined : manualSnippet(apiKey, baseUrl), }; } return { client: 'unknown', success: false, configPath: null, fallbackHint: manualSnippet(apiKey, baseUrl), }; } //# sourceMappingURL=mcp-config.js.map