Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
config-interactive.ts10.8 kB
/** * Interactive Configuration Command * Allows users to view and edit NCP settings interactively or via command-line */ import chalk from 'chalk'; import { createInterface } from 'readline'; import { loadGlobalSettings, saveGlobalSettings } from '../../utils/global-settings.js'; import { SettingsManager } from '../../services/scheduler/settings-manager.js'; interface ConfigOptions { autoImport: boolean; debugLogging: boolean; confirmModifications: boolean; enableScheduler: boolean; enableMCPManagement: boolean; logRotation: boolean; } /** * Known configuration keys and their types */ const KNOWN_KEYS: Record<string, 'boolean' | 'number' | 'string'> = { 'autoImport': 'boolean', 'debugLogging': 'boolean', 'confirmModifications': 'boolean', 'enableScheduler': 'boolean', 'enableMCPManagement': 'boolean', 'logRotation': 'boolean' }; /** * Parse string value to appropriate type */ function parseValue(value: string, type: 'boolean' | 'number' | 'string'): any { if (type === 'boolean') { const truthy = ['true', 'yes', 'on', '1', 'enabled']; const falsy = ['false', 'no', 'off', '0', 'disabled']; const lower = value.toLowerCase(); if (truthy.includes(lower)) return true; if (falsy.includes(lower)) return false; throw new Error(`Invalid boolean value: "${value}". Use true/false, yes/no, on/off, or 1/0`); } if (type === 'number') { const num = Number(value); if (isNaN(num)) throw new Error(`Invalid number value: "${value}"`); return num; } return value; } export class ConfigurationManager { private readline: any; constructor() { this.readline = createInterface({ input: process.stdin, output: process.stdout }); } /** * Display current configuration */ async showConfiguration(): Promise<void> { const globalSettings = await loadGlobalSettings(); const schedulerSettings = new SettingsManager(); const schedulerConfig = schedulerSettings.getConfig(); console.log(chalk.bold.cyan('\n📋 Current Configuration\n')); // Auto-import Client MCPs const autoImportEnabled = !process.env.NCP_SKIP_AUTO_IMPORT; const autoImportStatus = autoImportEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); console.log(`${autoImportStatus} ${chalk.bold('Auto-import Client MCPs')}`); console.log(` ${chalk.dim('Automatically import all MCPs from your MCP client on startup')}`); // Enable Debug Logging const debugEnabled = process.env.DEBUG === 'true' || process.env.NCP_DEBUG === 'true'; const debugStatus = debugEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); console.log(`\n${debugStatus} ${chalk.bold('Enable Debug Logging')}`); console.log(` ${chalk.dim('Show detailed logs for troubleshooting')}`); // Confirm Modifications Before Run const confirmEnabled = globalSettings.confirmBeforeRun.enabled; const confirmStatus = confirmEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); console.log(`\n${confirmStatus} ${chalk.bold('Confirm Modifications Before Run')}`); console.log(` ${chalk.dim('Show confirmation dialog before executing modification operations')}`); // Enable Scheduler const schedulerEnabled = schedulerConfig !== null; const schedulerStatus = schedulerEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); console.log(`\n${schedulerStatus} ${chalk.bold('Enable Scheduler (Built-in)')}`); console.log(` ${chalk.dim('Schedule tool executions with cron')}`); // Enable MCP Management const managementEnabled = true; // Always available const managementStatus = chalk.green('[✓]'); console.log(`\n${managementStatus} ${chalk.bold('Enable MCP Management (Built-in)')}`); console.log(` ${chalk.dim('Add, remove, and manage MCPs in your configuration')}`); // Log Rotation const logRotationEnabled = globalSettings.logRotation.enabled; const logRotationStatus = logRotationEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); console.log(`\n${logRotationStatus} ${chalk.bold('Enable Log Rotation')}`); console.log(` ${chalk.dim('Auto-rotate debug and protocol logs to prevent disk space issues')}`); console.log(` ${chalk.dim(`Advanced: Edit maxDebugFiles (${globalSettings.logRotation.maxDebugFiles}) and maxProtocolLines (${globalSettings.logRotation.maxProtocolLines}) in settings.json`)}`); // Code Mode const codeModeEnabled = globalSettings.enableCodeMode; const codeModeStatus = codeModeEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); const modeLabel = codeModeEnabled ? 'find-and-code' : 'find-and-run'; console.log(`\n${codeModeStatus} ${chalk.bold('Code Mode')}`); console.log(` ${chalk.dim(`Current mode: ${chalk.white(modeLabel)} (code tool always available internally)`)}`); // Skills const skillsEnabled = globalSettings.enableSkills; const skillsStatus = skillsEnabled ? chalk.green('[✓]') : chalk.gray('[✗]'); console.log(`\n${skillsStatus} ${chalk.bold('Skills (Built-in)')}`); console.log(` ${chalk.dim('Discover, install, and use Anthropic Agent Skills')}\n`); } /** * Ask user if they want to edit */ private async askToEdit(): Promise<boolean> { return new Promise((resolve) => { this.readline.question( chalk.bold('Do you want to edit? [y/N]: '), (answer: string) => { resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'); } ); }); } /** * Ask a yes/no question */ private async askYesNo(question: string, defaultValue: boolean): Promise<boolean> { return new Promise((resolve) => { const defaultAnswer = defaultValue ? 'Y/n' : 'y/N'; this.readline.question( chalk.cyan(`${question}? [${defaultAnswer}]: `), (answer: string) => { if (answer.toLowerCase() === 'y') resolve(true); else if (answer.toLowerCase() === 'n') resolve(false); else resolve(defaultValue); } ); }); } /** * Interactive edit mode */ async editConfiguration(): Promise<void> { console.log(chalk.bold.cyan('\n⚙️ Configuration Settings\n')); const globalSettings = await loadGlobalSettings(); // Auto-import Client MCPs const autoImport = await this.askYesNo('Auto-import Client MCPs', true); // Enable Debug Logging const debugLogging = await this.askYesNo('Enable Debug Logging', false); // Confirm Modifications Before Run const confirmModifications = await this.askYesNo('Confirm Modifications Before Run', true); // Enable Scheduler const enableScheduler = await this.askYesNo('Enable Scheduler (Built-in)', true); // Enable MCP Management const enableMCPManagement = await this.askYesNo('Enable MCP Management (Built-in)', true); // Enable Log Rotation const logRotation = await this.askYesNo('Enable Log Rotation', globalSettings.logRotation.enabled); // Enable Code Mode const codeMode = await this.askYesNo('Enable Code Mode (find-and-code vs find-and-run)', globalSettings.enableCodeMode); // Enable Skills const enableSkills = await this.askYesNo('Enable Skills (Built-in)', globalSettings.enableSkills); // Save configuration if (autoImport) { delete process.env.NCP_SKIP_AUTO_IMPORT; } else { process.env.NCP_SKIP_AUTO_IMPORT = 'true'; } if (debugLogging) { process.env.NCP_DEBUG = 'true'; } else { delete process.env.NCP_DEBUG; } globalSettings.confirmBeforeRun.enabled = confirmModifications; globalSettings.logRotation.enabled = logRotation; globalSettings.enableCodeMode = codeMode; globalSettings.enableSkills = enableSkills; // Save global settings await saveGlobalSettings(globalSettings); console.log(`\n${chalk.green('✅ Configuration saved')}\n`); } /** * Set a configuration value directly from command line * Usage: ncp config <key> <value> */ async setConfigValue(key: string, value: string): Promise<void> { try { // Check if key is known if (!KNOWN_KEYS[key]) { const availableKeys = Object.keys(KNOWN_KEYS).join(', '); console.error(chalk.red(`\n❌ Unknown configuration key: "${key}"\n`)); console.error(chalk.dim(`Available keys:\n ${availableKeys}\n`)); process.exit(1); } const expectedType = KNOWN_KEYS[key]; // Validate and parse value let parsedValue: any; try { parsedValue = parseValue(value, expectedType); } catch (parseError: any) { console.error(chalk.red(`\n❌ ${parseError.message}\n`)); process.exit(1); } // Apply the setting const globalSettings = await loadGlobalSettings(); switch (key) { case 'autoImport': if (parsedValue) { delete process.env.NCP_SKIP_AUTO_IMPORT; } else { process.env.NCP_SKIP_AUTO_IMPORT = 'true'; } break; case 'debugLogging': if (parsedValue) { process.env.NCP_DEBUG = 'true'; } else { delete process.env.NCP_DEBUG; } break; case 'confirmModifications': globalSettings.confirmBeforeRun.enabled = parsedValue; await saveGlobalSettings(globalSettings); break; case 'enableScheduler': // Scheduler is always available, but we could add a flag if needed if (!parsedValue) { console.warn(chalk.yellow('⚠️ Scheduler is always available. Setting ignored.\n')); } break; case 'enableMCPManagement': // MCP Management is always available if (!parsedValue) { console.warn(chalk.yellow('⚠️ MCP Management is always available. Setting ignored.\n')); } break; case 'logRotation': globalSettings.logRotation.enabled = parsedValue; await saveGlobalSettings(globalSettings); break; } console.log(chalk.green(`\n✅ Configuration updated: ${chalk.bold(key)} = ${chalk.cyan(value)}\n`)); } catch (error: any) { console.error(chalk.red(`\n❌ Error: ${error.message}\n`)); process.exit(1); } } /** * Main entry point - show and optionally edit */ async run(): Promise<void> { try { await this.showConfiguration(); const shouldEdit = await this.askToEdit(); if (shouldEdit) { await this.editConfiguration(); } this.readline.close(); } catch (error: any) { console.error(chalk.red(`\n❌ Error: ${error.message}\n`)); this.readline.close(); process.exit(1); } } }

Latest Blog Posts

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/portel-dev/ncp'

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