Skip to main content
Glama

Agent MCP

practical.ts33.6 kB
// Practical TUI for Agent-MCP - No animations, focus on functionality // Handles embedding providers, CLI selection, and tool management efficiently import inquirer from 'inquirer'; import { ToolCategories, PREDEFINED_MODES, DEFAULT_CONFIG, loadToolConfig, saveToolConfig, getConfigMode, validateToolConfig, getCategoryDescription } from '../core/toolConfig.js'; import { TUIColors, setProjectDir } from '../core/config.js'; import { loadExtendedConfig, saveExtendedConfig, validateEmbeddingProviderConfig, ExtendedConfig, loadNamedConfigs, saveNamedConfig, loadNamedConfig, deleteNamedConfig, NamedConfig } from '../core/extendedConfig.js'; import { isPortAvailable, findAvailablePort, getPortRecommendations } from '../core/portChecker.js'; import { getCurrentEnvValues, setEnvVariable, validateApiKey, maskApiKey, MANAGED_ENV_VARS, EnvVariable, getEnvVariablesByCategory, reloadEnvironmentVariables } from '../core/envManager.js'; // Available embedding providers export const EMBEDDING_PROVIDERS = { openai: { name: 'OpenAI', description: 'OpenAI text-embedding-3-large (requires OPENAI_API_KEY)', envVar: 'OPENAI_API_KEY', recommended: true }, ollama: { name: 'Ollama', description: 'Local Ollama embeddings (requires Ollama running)', envVar: null, recommended: true }, huggingface: { name: 'HuggingFace', description: 'HuggingFace models (API key or local transformers)', envVar: 'HUGGINGFACE_API_KEY', recommended: false }, gemini: { name: 'Google Gemini', description: 'Google Gemini embeddings (requires GEMINI_API_KEY)', envVar: 'GEMINI_API_KEY', recommended: false }, localserver: { name: 'Local Server', description: 'Custom local embedding server', envVar: null, recommended: false } } as const; // Available CLI agents for background tasks export const CLI_AGENTS = { claude: { name: 'Claude CLI', command: 'claude', description: 'Anthropic Claude (recommended, full MCP support)', recommended: true, mcpSupport: true }, gemini: { name: 'Gemini CLI', command: 'gemini', description: 'Google Gemini (MCP support via settings.json)', recommended: true, mcpSupport: true }, llxprt: { name: 'LLXprt CLI', command: 'llxprt', description: 'LLXprt multi-provider client (full MCP support)', recommended: false, mcpSupport: true }, swarmcode: { name: 'SwarmCode CLI', command: 'swarmcode', description: 'SwarmCode multi-agent system (MCP support)', recommended: false, mcpSupport: true } } as const; function displayHeader() { console.clear(); console.log(`${TUIColors.HEADER}${TUIColors.BOLD}╔══════════════════════════════════════════════════════════════╗`); console.log(`║ Agent-MCP Configuration ║`); console.log(`║ Practical Setup ║`); console.log(`╚══════════════════════════════════════════════════════════════╝${TUIColors.ENDC}`); console.log(); } async function handleNamedConfigurations(): Promise<ExtendedConfig | null> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}📋 Configuration Management${TUIColors.ENDC}`); console.log(); const namedConfigs = loadNamedConfigs(); if (namedConfigs.length === 0) { console.log('No saved configurations found.'); console.log(); return null; } console.log('Saved Configurations:'); namedConfigs.forEach(config => { const lastUsed = config.lastUsed ? new Date(config.lastUsed).toLocaleDateString() : 'Never used'; console.log(` • ${TUIColors.OKGREEN}${config.name}${TUIColors.ENDC} - ${config.description}`); console.log(` Created: ${new Date(config.createdAt).toLocaleDateString()}, Last used: ${lastUsed}`); }); console.log(); const configAction = await inquirer.prompt({ type: 'list', name: 'action', message: 'What would you like to do?', choices: [ { name: 'Load a saved configuration', value: 'load' }, { name: 'Create new configuration', value: 'new' }, { name: 'Delete a saved configuration', value: 'delete' } ] }); if (configAction.action === 'load') { const configChoice = await inquirer.prompt({ type: 'list', name: 'config', message: 'Select configuration to load:', choices: namedConfigs.map(config => ({ name: `${config.name} - ${config.description}`, value: config.name, short: config.name })) }); const selectedConfig = loadNamedConfig(configChoice.config); if (selectedConfig) { console.log(`${TUIColors.OKGREEN}✅ Loaded configuration: ${selectedConfig.name}${TUIColors.ENDC}`); return selectedConfig.config; } } if (configAction.action === 'delete') { const configChoice = await inquirer.prompt({ type: 'list', name: 'config', message: 'Select configuration to delete:', choices: namedConfigs.map(config => ({ name: `${config.name} - ${config.description}`, value: config.name, short: config.name })) }); const confirm = await inquirer.prompt({ type: 'confirm', name: 'delete', message: `Delete configuration '${configChoice.config}'?`, default: false }); if (confirm.delete) { deleteNamedConfig(configChoice.config); console.log(`${TUIColors.OKGREEN}✅ Configuration deleted${TUIColors.ENDC}`); } return await handleNamedConfigurations(); } return null; } async function getTabCompletion(partialPath: string): Promise<{ completion: string; suggestions: string[] }> { try { const fs = await import('fs'); const path = await import('path'); const os = await import('os'); let searchPath = partialPath; let prefix = ''; // Handle home directory expansion if (partialPath.startsWith('~')) { searchPath = partialPath.replace('~', os.homedir()); } // If path doesn't exist, get parent directory and prefix if (!fs.existsSync(searchPath)) { prefix = path.basename(searchPath); searchPath = path.dirname(searchPath); } if (!fs.existsSync(searchPath)) { return { completion: partialPath, suggestions: [] }; } const entries = fs.readdirSync(searchPath, { withFileTypes: true }); const directories = entries .filter(entry => entry.isDirectory()) .map(entry => entry.name) .filter(name => prefix ? name.toLowerCase().startsWith(prefix.toLowerCase()) : true) .sort(); if (directories.length === 0) { return { completion: partialPath, suggestions: [] }; } // If only one match, complete it if (directories.length === 1) { const firstDir = directories[0]; if (!firstDir) { return { completion: partialPath, suggestions: [] }; } const completedPath = path.join(searchPath, firstDir); return { completion: completedPath.endsWith('/') ? completedPath : completedPath + '/', suggestions: [completedPath] }; } // Multiple matches - find common prefix let commonPrefix = directories[0] || ''; for (let i = 1; i < directories.length; i++) { const currentDir = directories[i]; if (!currentDir) continue; let j = 0; while (j < commonPrefix.length && j < currentDir.length && commonPrefix[j]?.toLowerCase() === currentDir[j]?.toLowerCase()) { j++; } commonPrefix = commonPrefix.substring(0, j); } const suggestions = directories.map(dir => path.join(searchPath, dir)); if (commonPrefix && commonPrefix.length > prefix.length) { const completedPath = path.join(searchPath, commonPrefix); return { completion: completedPath, suggestions }; } return { completion: partialPath, suggestions }; } catch (error) { return { completion: partialPath, suggestions: [] }; } } async function selectProjectDirectoryWithTabCompletion(): Promise<string> { const fs = await import('fs'); const path = await import('path'); const os = await import('os'); console.log(`${TUIColors.DIM}💡 Type path and press TAB to complete, ENTER when done${TUIColors.ENDC}`); console.log(`${TUIColors.DIM} Use '~' for home directory, Ctrl+C to cancel${TUIColors.ENDC}`); console.log(); return new Promise((resolve, reject) => { let currentInput = ''; let cursorPosition = 0; // Set raw mode to capture individual key presses if (process.stdin.setRawMode) { process.stdin.setRawMode(true); } process.stdin.resume(); // Display initial prompt process.stdout.write('📁 Enter project directory path: '); const cleanup = () => { if (process.stdin.setRawMode) { process.stdin.setRawMode(false); } process.stdin.removeAllListeners('data'); }; process.stdin.on('data', async (key) => { const keyStr = key.toString(); // Handle Ctrl+C if (key[0] === 3) { cleanup(); console.log('\n'); reject(new Error('Directory selection cancelled')); return; } // Handle Enter if (keyStr === '\r' || keyStr === '\n') { cleanup(); console.log('\n'); let finalPath = currentInput.trim(); if (!finalPath) { reject(new Error('No path provided')); return; } // Expand home directory if (finalPath.startsWith('~')) { finalPath = finalPath.replace('~', os.homedir()); } // Validate path try { if (!path.isAbsolute(finalPath)) { console.log(`${TUIColors.FAIL}❌ Path must be absolute${TUIColors.ENDC}`); reject(new Error('Path must be absolute')); return; } if (!fs.existsSync(finalPath)) { console.log(`${TUIColors.FAIL}❌ Directory does not exist: ${finalPath}${TUIColors.ENDC}`); reject(new Error('Directory does not exist')); return; } const stats = fs.statSync(finalPath); if (!stats.isDirectory()) { console.log(`${TUIColors.FAIL}❌ Path is not a directory: ${finalPath}${TUIColors.ENDC}`); reject(new Error('Path is not a directory')); return; } resolve(finalPath); } catch (error) { console.log(`${TUIColors.FAIL}❌ Invalid path: ${finalPath}${TUIColors.ENDC}`); reject(error); } return; } // Handle TAB (ASCII 9) if (key[0] === 9) { const result = await getTabCompletion(currentInput); if (result.suggestions.length === 0) { // No matches - beep process.stdout.write('\x07'); } else if (result.suggestions.length === 1) { // Single match - complete it const completion = result.completion; // Clear current line and rewrite with completion process.stdout.write('\r\x1b[K'); // Clear line process.stdout.write('📁 Enter project directory path: ' + completion); currentInput = completion; cursorPosition = completion.length; } else { // Multiple matches - show options console.log('\n'); const suggestions = result.suggestions.map(s => path.basename(s)).join(' '); console.log(`${TUIColors.DIM}Options: ${suggestions}${TUIColors.ENDC}`); // Complete to common prefix if any if (result.completion !== currentInput) { currentInput = result.completion; cursorPosition = currentInput.length; } // Redraw prompt process.stdout.write('📁 Enter project directory path: ' + currentInput); } return; } // Handle Delete key (escape sequence \x1b[3~) if (keyStr === '\x1b[3~') { // Delete character at cursor (for now just ignore since we don't track cursor properly) return; } // Handle Arrow keys and other escape sequences if (keyStr.startsWith('\x1b[')) { // Ignore arrow keys and other escape sequences for now return; } // Handle Backspace (ASCII 8 or 127) if (key[0] === 8 || key[0] === 127) { if (currentInput.length > 0) { currentInput = currentInput.slice(0, -1); cursorPosition = currentInput.length; // Move cursor back and clear process.stdout.write('\b \b'); } return; } // Handle printable characters if (keyStr.length === 1 && keyStr >= ' ' && keyStr <= '~') { currentInput += keyStr; cursorPosition = currentInput.length; process.stdout.write(keyStr); return; } // Ignore other non-printable characters }); }); } async function selectProjectDirectory(defaultDir?: string): Promise<string> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}📁 Project Directory Configuration${TUIColors.ENDC}`); console.log('This is the directory where agents will operate and execute tasks.'); console.log(); const currentDir = defaultDir || process.cwd(); if (defaultDir) { console.log(`Saved directory: ${TUIColors.OKGREEN}${defaultDir}${TUIColors.ENDC}`); } else { console.log(`Current directory: ${TUIColors.OKGREEN}${currentDir}${TUIColors.ENDC}`); } console.log(); const response = await inquirer.prompt({ type: 'list', name: 'choice', message: 'Select project directory for agents:', choices: [ { name: defaultDir ? `Use saved directory (${currentDir})` : `Use current directory (${currentDir})`, value: 'current' }, { name: 'Browse with tab completion (type and TAB)', value: 'browse' }, { name: 'Enter custom path manually', value: 'manual' } ] }); if (response.choice === 'browse') { try { const selectedPath = await selectProjectDirectoryWithTabCompletion(); console.log(`${TUIColors.OKGREEN}✅ Project directory set to: ${selectedPath}${TUIColors.ENDC}`); console.log(); return selectedPath; } catch (error) { console.log(`${TUIColors.WARNING}⚠️ Selection cancelled, using current directory${TUIColors.ENDC}`); console.log(); return currentDir; } } if (response.choice === 'manual') { const customPath = await inquirer.prompt({ type: 'input', name: 'path', message: 'Enter absolute path to project directory:', default: currentDir, validate: async (input) => { if (!input || input.trim() === '') { return 'Path is required'; } const trimmedPath = input.trim(); const fs = await import('fs'); const path = await import('path'); const os = await import('os'); let finalPath = trimmedPath; if (finalPath.startsWith('~')) { finalPath = finalPath.replace('~', os.homedir()); } if (!path.isAbsolute(finalPath)) { return 'Please enter an absolute path (starting with / or C:\\)'; } if (!fs.existsSync(finalPath)) { return `Directory does not exist: ${finalPath}`; } const stats = fs.statSync(finalPath); if (!stats.isDirectory()) { return `Path is not a directory: ${finalPath}`; } return true; } }); let finalPath = customPath.path.trim(); if (finalPath.startsWith('~')) { const os = await import('os'); finalPath = finalPath.replace('~', os.homedir()); } console.log(`${TUIColors.OKGREEN}✅ Project directory set to: ${finalPath}${TUIColors.ENDC}`); console.log(); return finalPath; } console.log(`${TUIColors.OKGREEN}✅ Using current directory as project directory${TUIColors.ENDC}`); console.log(); return currentDir; } async function selectServerPort(defaultPort?: number): Promise<number> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}🌐 Server Port Configuration${TUIColors.ENDC}`); console.log(); if (defaultPort) { const isAvailable = await isPortAvailable(defaultPort); if (isAvailable) { console.log(`Saved port ${defaultPort}: ${TUIColors.OKGREEN}✅ Available${TUIColors.ENDC}`); console.log(); const useDefault = await inquirer.prompt({ type: 'confirm', name: 'use', message: `Use saved port ${defaultPort}?`, default: true }); if (useDefault.use) { return defaultPort; } } else { console.log(`${TUIColors.WARNING}⚠️ Saved port ${defaultPort} is not available${TUIColors.ENDC}`); console.log(); } } const availablePorts = await getPortRecommendations(); if (availablePorts.length === 0) { console.log(`${TUIColors.WARNING}⚠️ No common ports available, finding alternative...${TUIColors.ENDC}`); return await findAvailablePort(3001, 9999); } console.log('Available Ports Found:'); availablePorts.forEach(port => { console.log(` Port ${port}: ${TUIColors.OKGREEN}✅ Available${TUIColors.ENDC}`); }); console.log(); const choices = [ ...availablePorts.map(port => ({ name: `Port ${port} - Available`, value: port, short: `${port}` })), { name: 'Enter custom port', value: 'custom', short: 'Custom' } ]; const portChoice = await inquirer.prompt({ type: 'list', name: 'port', message: 'Select server port:', choices }); if (portChoice.port === 'custom') { const customPort = await inquirer.prompt({ type: 'number', name: 'port', message: 'Enter port number (1024-65535):', validate: async (input) => { if (!input || input < 1024 || input > 65535) { return 'Port must be between 1024 and 65535'; } if (!(await isPortAvailable(input))) { return `Port ${input} is already in use`; } return true; } }); return customPort.port; } return portChoice.port; } async function configureApiKeys(embeddingProvider: string): Promise<void> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}🔑 API Key Configuration${TUIColors.ENDC}`); console.log(); const currentEnvVars = getCurrentEnvValues(); const embeddingVars = getEnvVariablesByCategory('embedding'); const cliVars = getEnvVariablesByCategory('cli'); const neededKeys = embeddingVars.filter(envVar => { switch (embeddingProvider) { case 'openai': return envVar.key === 'OPENAI_API_KEY'; case 'huggingface': return envVar.key === 'HUGGINGFACE_API_KEY'; case 'gemini': return envVar.key === 'GEMINI_API_KEY'; default: return false; } }); neededKeys.push(...cliVars); if (neededKeys.length === 0) { console.log(`${TUIColors.OKGREEN}✅ No API keys required for ${embeddingProvider} provider${TUIColors.ENDC}`); console.log(); return; } console.log('Current API Key Status:'); for (const envVar of neededKeys) { const currentValue = currentEnvVars.get(envVar.key); const status = currentValue ? `${TUIColors.OKGREEN}✅ Set (${maskApiKey(currentValue)})${TUIColors.ENDC}` : `${TUIColors.WARNING}❌ Not set${TUIColors.ENDC}`; console.log(` ${envVar.key}: ${status}`); } console.log(); const configureKeys = await inquirer.prompt({ type: 'confirm', name: 'configure', message: 'Do you want to configure API keys now?', default: true }); if (!configureKeys.configure) { return; } for (const envVar of neededKeys) { const currentValue = currentEnvVars.get(envVar.key); console.log(`\n${TUIColors.OKBLUE}Configuring ${envVar.key}${TUIColors.ENDC}`); console.log(`Description: ${envVar.description}`); if (currentValue) { console.log(`Current value: ${maskApiKey(currentValue)}`); } const keyConfig = await inquirer.prompt([ { type: 'list', name: 'action', message: `What would you like to do with ${envVar.key}?`, choices: [ { name: currentValue ? 'Keep current value' : 'Skip for now', value: 'keep' }, { name: 'Set new value', value: 'set' }, { name: 'Clear value', value: 'clear', disabled: !currentValue } ] } ]); if (keyConfig.action === 'set') { const keyValue = await inquirer.prompt({ type: 'password', name: 'value', message: `Enter ${envVar.key}:`, mask: '*', validate: (input) => { const validation = validateApiKey(envVar.key, input); if (!validation.valid) { return validation.message || 'Invalid API key format'; } return true; } }); setEnvVariable(envVar.key, keyValue.value); console.log(`${TUIColors.OKGREEN}✅ ${envVar.key} saved successfully${TUIColors.ENDC}`); } else if (keyConfig.action === 'clear' && currentValue) { setEnvVariable(envVar.key, ''); console.log(`${TUIColors.WARNING}🗑️ ${envVar.key} cleared${TUIColors.ENDC}`); } } reloadEnvironmentVariables(); console.log(`\n${TUIColors.OKGREEN}✅ API key configuration complete${TUIColors.ENDC}`); console.log(); } async function selectEmbeddingProvider(): Promise<string> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}📊 Embedding Provider Configuration${TUIColors.ENDC}`); console.log(); const providerChoices = Object.entries(EMBEDDING_PROVIDERS).map(([key, provider]) => ({ name: `${provider.recommended ? '⭐ ' : ' '}${provider.name} - ${provider.description}`, value: key, short: provider.name })); const response = await inquirer.prompt({ type: 'list', name: 'provider', message: 'Select your embedding provider:', choices: providerChoices, pageSize: 10 }); await configureApiKeys(response.provider); return response.provider; } async function selectCLIAgents(): Promise<string[]> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}🤖 CLI Agent Selection${TUIColors.ENDC}`); console.log('Choose which CLI agents you want available for background tasks:'); console.log(); const cliChoices = Object.entries(CLI_AGENTS).map(([key, agent]) => ({ name: `${agent.recommended ? '⭐ ' : ' '}${agent.name} - ${agent.description}${agent.mcpSupport ? ' [MCP]' : ''}`, value: key, checked: agent.recommended })); const response = await inquirer.prompt({ type: 'checkbox', name: 'agents', message: 'Select CLI agents (space to toggle, enter to confirm):', choices: cliChoices, validate: (input) => { if (input.length === 0) { return 'Please select at least one CLI agent'; } return true; } }); console.log(); return response.agents; } async function selectConfigurationMode(): Promise<ToolCategories | 'custom'> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}⚙️ Tool Configuration${TUIColors.ENDC}`); console.log(); const modeChoices = Object.entries(PREDEFINED_MODES).map(([key, mode]) => ({ name: `${mode.name} - ${mode.description}`, value: key, short: mode.name })); modeChoices.push({ name: 'Custom Configuration - Choose individual tool categories', value: 'custom', short: 'Custom' }); const response = await inquirer.prompt({ type: 'list', name: 'mode', message: 'Select configuration mode:', choices: modeChoices, pageSize: 10 }); if (response.mode === 'custom') { return 'custom'; } const selectedMode = PREDEFINED_MODES[response.mode]; if (!selectedMode) { throw new Error(`Unknown mode: ${response.mode}`); } return selectedMode.categories; } async function selectCustomTools(): Promise<ToolCategories> { console.log(`${TUIColors.OKBLUE}${TUIColors.BOLD}🔧 Custom Tool Selection${TUIColors.ENDC}`); console.log(); const currentConfig = loadToolConfig(); const toolChoices = [ { key: 'basic' as keyof ToolCategories, name: 'Basic Tools', description: 'Health checks, system status (always enabled)', disabled: true }, { key: 'rag' as keyof ToolCategories, name: 'RAG/Vector Search', description: 'Document search and knowledge retrieval' }, { key: 'memory' as keyof ToolCategories, name: 'Memory & Context', description: 'Project context and session memory' }, { key: 'agentManagement' as keyof ToolCategories, name: 'Agent Management', description: 'Create, manage, and coordinate agents' }, { key: 'taskManagement' as keyof ToolCategories, name: 'Task Management', description: 'Task creation and hierarchical workflows' }, { key: 'fileManagement' as keyof ToolCategories, name: 'File Management', description: 'File operations and project management' }, { key: 'agentCommunication' as keyof ToolCategories, name: 'Agent Communication', description: 'Inter-agent messaging and coordination' }, { key: 'sessionState' as keyof ToolCategories, name: 'Session State', description: 'Session persistence and recovery' }, { key: 'assistanceRequest' as keyof ToolCategories, name: 'Assistance Requests', description: 'Intelligent help and support' }, { key: 'backgroundAgents' as keyof ToolCategories, name: 'Background Agents', description: 'Standalone agents without task hierarchy' } ]; const checkboxChoices = toolChoices.map(tool => ({ name: tool.disabled ? `${tool.name} - ${tool.description} [REQUIRED]` : `${tool.name} - ${tool.description}`, value: tool.key, checked: currentConfig[tool.key], disabled: tool.disabled })); const response = await inquirer.prompt({ type: 'checkbox', name: 'tools', message: 'Select tool categories (space to toggle, enter to confirm):', choices: checkboxChoices, pageSize: 15 }); const config: ToolCategories = { basic: true, rag: false, memory: false, agentManagement: false, taskManagement: false, fileManagement: false, agentCommunication: false, sessionState: false, assistanceRequest: false, backgroundAgents: false }; response.tools.forEach((tool: string) => { config[tool as keyof ToolCategories] = true; }); console.log(); return config; } function displayConfigurationSummary( config: ToolCategories, embeddingProvider: string, cliAgents: string[], serverPort: number, projectDirectory: string ) { console.log(`${TUIColors.HEADER}${TUIColors.BOLD}📋 Configuration Summary${TUIColors.ENDC}`); console.log('━'.repeat(50)); console.log(); console.log(`${TUIColors.OKBLUE}📁 Project Directory:${TUIColors.ENDC} ${projectDirectory}`); console.log(`${TUIColors.OKBLUE}🌐 Server Port:${TUIColors.ENDC} ${serverPort}`); console.log(); const provider = EMBEDDING_PROVIDERS[embeddingProvider as keyof typeof EMBEDDING_PROVIDERS]; console.log(`${TUIColors.OKBLUE}📊 Embedding Provider:${TUIColors.ENDC} ${provider.name}`); console.log(); console.log(`${TUIColors.OKBLUE}🤖 Available CLI Agents:${TUIColors.ENDC}`); cliAgents.forEach(agentKey => { const agent = CLI_AGENTS[agentKey as keyof typeof CLI_AGENTS]; console.log(` • ${agent.name}${agent.mcpSupport ? ' [MCP]' : ''}`); }); console.log(); console.log(`${TUIColors.OKBLUE}🔧 Enabled Tool Categories:${TUIColors.ENDC}`); Object.entries(config) .filter(([_, enabled]) => enabled) .forEach(([category, _]) => { console.log(` • ${getCategoryDescription(category as keyof ToolCategories)}`); }); console.log(); console.log('━'.repeat(50)); } export async function launchPracticalConfigurationTUI(): Promise<{ toolConfig: ToolCategories; embeddingProvider: string; cliAgents: string[]; serverPort: number; projectDirectory: string; configName?: string; }> { displayHeader(); const existingConfig = await handleNamedConfigurations(); if (existingConfig) { console.log(`${TUIColors.OKGREEN}✅ Loaded saved configuration: ${existingConfig.configName}${TUIColors.ENDC}`); console.log(); // Use saved project directory and port as defaults const projectDirectory = await selectProjectDirectory(existingConfig.projectDirectory); const serverPort = await selectServerPort(existingConfig.serverPort); // Set project directory so configs are saved to the right place setProjectDir(projectDirectory); return { toolConfig: existingConfig.toolCategories, embeddingProvider: existingConfig.embeddingProvider, cliAgents: existingConfig.cliAgents, serverPort: serverPort, projectDirectory: projectDirectory, configName: existingConfig.configName }; } const projectDirectory = await selectProjectDirectory(); const serverPort = await selectServerPort(); const embeddingProvider = await selectEmbeddingProvider(); const cliAgents = await selectCLIAgents(); const configMode = await selectConfigurationMode(); let toolConfig: ToolCategories; if (configMode === 'custom') { toolConfig = await selectCustomTools(); } else { toolConfig = configMode as ToolCategories; } displayConfigurationSummary(toolConfig, embeddingProvider, cliAgents, serverPort, projectDirectory); const confirm = await inquirer.prompt({ type: 'confirm', name: 'proceed', message: 'Proceed with this configuration?', default: true }); if (!confirm.proceed) { console.log(`${TUIColors.WARNING}⚠️ Configuration cancelled${TUIColors.ENDC}`); process.exit(0); } const saveConfig = await inquirer.prompt({ type: 'confirm', name: 'save', message: 'Would you like to save this configuration for future use?', default: true }); let configName: string | undefined; if (saveConfig.save) { const configDetails = await inquirer.prompt([ { type: 'input', name: 'name', message: 'Enter a name for this configuration:', validate: (input) => { if (!input || input.trim() === '') { return 'Configuration name is required'; } if (input.length > 50) { return 'Configuration name must be 50 characters or less'; } return true; } }, { type: 'input', name: 'description', message: 'Enter a description (optional):', default: `Custom configuration created ${new Date().toLocaleDateString()}` } ]); configName = configDetails.name.trim(); const namedConfigData: ExtendedConfig = { toolCategories: toolConfig, embeddingProvider, cliAgents, serverPort, projectDirectory, configName, advancedSettings: { embeddingModel: 'text-embedding-3-large', embeddingDimensions: 1536, maxBatchSize: 100, cliAgentTimeout: 30000, defaultCLI: cliAgents.includes('claude') ? 'claude' : (cliAgents[0] || 'claude') }, lastUpdated: new Date().toISOString() }; saveNamedConfig(configName || 'unnamed', configDetails.description || '', namedConfigData); console.log(`${TUIColors.OKGREEN}✅ Configuration '${configName}' saved successfully${TUIColors.ENDC}`); } const toolValidation = validateToolConfig(toolConfig); if (!toolValidation.valid) { console.log(`${TUIColors.FAIL}❌ Tool configuration warnings:${TUIColors.ENDC}`); toolValidation.warnings.forEach((warning: string) => console.log(` • ${warning}`)); console.log(); } const providerValidation = validateEmbeddingProviderConfig(embeddingProvider); if (!providerValidation.valid) { console.log(`${TUIColors.WARNING}⚠️ Embedding provider warnings:${TUIColors.ENDC}`); providerValidation.errors.forEach(error => console.log(` • ${error}`)); console.log(); } const extendedConfig: ExtendedConfig = { toolCategories: toolConfig, embeddingProvider, cliAgents, serverPort, projectDirectory, configName, advancedSettings: { embeddingModel: 'text-embedding-3-large', embeddingDimensions: 1536, maxBatchSize: 100, cliAgentTimeout: 30000, defaultCLI: cliAgents.includes('claude') ? 'claude' : (cliAgents[0] || 'claude') }, lastUpdated: new Date().toISOString() }; // Set project directory BEFORE saving configs so they go to the right place setProjectDir(projectDirectory); saveExtendedConfig(extendedConfig); saveToolConfig(toolConfig); setEnvVariable('EMBEDDING_PROVIDER', embeddingProvider); console.log(`${TUIColors.OKGREEN}✅ Configuration saved successfully${TUIColors.ENDC}`); console.log(); return { toolConfig, embeddingProvider, cliAgents, serverPort, projectDirectory, configName }; }

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/rinadelph/Agent-MCP'

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