UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

686 lines (671 loc) 89.6 kB
/** * Configuration handler for the dollhouse_config MCP tool * Provides unified interface for all configuration management operations */ import { SecureErrorHandler } from '../security/errorHandler.js'; import { getFriendlyNullValue } from '../config/wizardTemplates.js'; import { getSourcePriorityConfig, saveSourcePriorityConfig, validateSourcePriority, parseSourcePriorityOrder, getSourceDisplayName, DEFAULT_SOURCE_PRIORITY } from '../config/sourcePriority.js'; import { validateCustomFormat } from '../config/indicator-config.js'; import { AgentSkillConverter, } from '../converters/AgentSkillConverter.js'; import * as yaml from 'js-yaml'; /** Valid indicator style values */ const VALID_INDICATOR_STYLES = ['full', 'minimal', 'compact', 'custom']; /** Valid bracket style values */ const VALID_BRACKET_STYLES = ['square', 'round', 'curly', 'angle', 'none']; /** Mapping from dot-notation paths to IndicatorConfig property names */ const INDICATOR_PATH_MAP = { 'display.indicator.enabled': 'enabled', 'display.indicator.style': 'style', 'display.indicator.customFormat': 'customFormat', 'display.indicator.showEmoji': 'showEmoji', 'display.indicator.showName': 'showName', 'display.indicator.showVersion': 'showVersion', 'display.indicator.showAuthor': 'showAuthor', 'display.indicator.showCategory': 'showCategory', 'display.indicator.separator': 'separator', 'display.indicator.emoji': 'emoji', 'display.indicator.bracketStyle': 'bracketStyle', }; export class ConfigHandler { configManager; initService; indicatorService; agentSkillConverter; constructor(configManager, initService, indicatorService, agentSkillConverter = new AgentSkillConverter()) { this.configManager = configManager; this.initService = initService; this.indicatorService = indicatorService; this.agentSkillConverter = agentSkillConverter; } /** * Convert between current Agent Skill and Dollhouse Skill formats. * * This operation is pure/in-memory: it does not read or write files. * Use `roundtrip_state` for lossless reverse conversion of supported fields. */ async convertSkillFormat(options) { return this.agentSkillConverter.convert(options); } /** * Handle configuration operations via the dollhouse_config tool * * @param options - Configuration operation options * @param options.action - The action to perform (get, set, reset, export, import, wizard) * @param options.setting - Optional setting path for get/set operations * @param options.value - Optional value for set operations * @param options.section - Optional section for filtering * @param options.format - Optional format for export (yaml or json) * @param options.data - Optional data for import operations * @returns Promise resolving to content object with operation result * @async * * @note The wizard action is async and will await the handleWizard method * @since v1.4.0 - handleWizard made async for better config fetching */ async handleConfigOperation(options) { const indicator = this.indicatorService.getPersonaIndicator(); try { await this.initService.ensureInitialized(); await this.configManager.initialize(); switch (options.action) { case 'get': return this.handleGet(options, indicator); case 'set': return this.handleSet(options, indicator); case 'reset': return this.handleReset(options, indicator); case 'export': return this.handleExport(options, indicator); case 'import': return this.handleImport(options, indicator); case 'wizard': return await this.handleWizard(indicator); default: return { content: [{ type: "text", text: `${indicator}❌ Invalid action '${options.action}'.\n\n` + `Valid actions: get, set, reset, export, import, wizard` }] }; } } catch (error) { const sanitizedError = SecureErrorHandler.sanitizeError(error); return { content: [{ type: "text", text: `${indicator}❌ Configuration operation failed: ${sanitizedError.message}` }] }; } } async handleGet(options, indicator) { // Get a specific setting or all settings if (options.setting) { // Handle source_priority as a special case if (options.setting === 'source_priority' || options.setting === 'source.priority') { const config = getSourcePriorityConfig(); return { content: [{ type: "text", text: this.formatSourcePriorityConfig(config, indicator) }] }; } const value = this.configManager.getSetting(options.setting); if (value === undefined) { return { content: [{ type: "text", text: `${indicator}❌ Setting '${options.setting}' not found.\n\n` + `Use \`dollhouse_config action: "get"\` to see all available settings.` }] }; } return { content: [{ type: "text", text: `${indicator}⚙️ **Configuration Setting**\n\n` + `**${options.setting}**: ${this.formatValue(value)}` }] }; } // Get all settings - make them user-friendly const config = this.configManager.getConfig(); const friendlyConfig = this.makeFriendlyConfig(config); // Add source_priority to the output if not already present if (!friendlyConfig.source_priority) { const sourcePriorityConfig = getSourcePriorityConfig(); friendlyConfig.source_priority = { order: sourcePriorityConfig.priority, stop_on_first: sourcePriorityConfig.stopOnFirst, check_all_for_updates: sourcePriorityConfig.checkAllForUpdates, fallback_on_error: sourcePriorityConfig.fallbackOnError }; } return { content: [{ type: "text", text: `${indicator}⚙️ **DollhouseMCP Configuration**\n\n` + `\`\`\`yaml\n${yaml.dump(friendlyConfig, { lineWidth: -1 })}\`\`\`` }] }; } async handleSet(options, indicator) { // Set a configuration value if (!options.setting || options.value === undefined) { return { content: [{ type: "text", text: `${indicator}❌ Both 'setting' and 'value' are required for set operation.\n\n` + `Example: \`dollhouse_config action: "set", setting: "sync.enabled", value: true\`` }] }; } // Handle source_priority settings if (options.setting.startsWith('source_priority') || options.setting.startsWith('source.priority')) { return await this.handleSourcePrioritySet(options, indicator); } // Handle display.indicator.* settings with validation and runtime update if (options.setting.startsWith('display.indicator.')) { return await this.handleIndicatorSet(options, indicator); } // Type coercion for common string-to-type conversions let coercedValue = options.value; // Convert string booleans to actual booleans if (typeof coercedValue === 'string') { const lowerValue = coercedValue.toLowerCase(); if (lowerValue === 'true') { coercedValue = true; } else if (lowerValue === 'false') { coercedValue = false; } else if (/^\d+$/.test(coercedValue)) { // Convert numeric strings to numbers const numValue = Number.parseInt(coercedValue, 10); if (!Number.isNaN(numValue)) { coercedValue = numValue; } } } await this.configManager.updateSetting(options.setting, coercedValue); return { content: [{ type: "text", text: `${indicator}✅ **Configuration Updated**\n\n` + `**${options.setting}** set to: ${JSON.stringify(coercedValue, null, 2)}\n\n` + `Changes have been saved to the configuration file.` }] }; } async handleReset(options, indicator) { // Reset configuration to defaults if (options.section) { // Handle source_priority reset if (options.section === 'source_priority' || options.section === 'source.priority') { await saveSourcePriorityConfig(DEFAULT_SOURCE_PRIORITY); return { content: [{ type: "text", text: `${indicator}🔄 **Source Priority Reset**\n\n` + `Source priority has been reset to default values:\n\n` + this.formatSourcePriorityConfig(DEFAULT_SOURCE_PRIORITY, '') }] }; } await this.configManager.resetConfig(options.section); return { content: [{ type: "text", text: `${indicator}🔄 **Configuration Reset**\n\n` + `Section '${options.section}' has been reset to default values.` }] }; } // Reset all configuration (including source_priority) await this.configManager.resetConfig(); // Also reset source_priority to defaults await saveSourcePriorityConfig(DEFAULT_SOURCE_PRIORITY); return { content: [{ type: "text", text: `${indicator}🔄 **Configuration Reset**\n\n` + `All settings have been reset to default values.\n\n` + `Note: User identity and GitHub authentication are preserved.` }] }; } async handleExport(options, indicator) { // Export configuration const format = options.format || 'yaml'; const exported = await this.configManager.exportConfig(format); return { content: [{ type: "text", text: `${indicator}📤 **Configuration Export**\n\n` + `\`\`\`${format}\n${exported}\`\`\`\n\n` + `You can save this configuration and import it later.` }] }; } async handleImport(options, indicator) { // Import configuration if (!options.data) { return { content: [{ type: "text", text: `${indicator}❌ Configuration data is required for import.\n\n` + `Provide YAML or JSON configuration in the 'data' parameter.` }] }; } await this.configManager.importConfig(options.data); return { content: [{ type: "text", text: `${indicator}✅ **Configuration Imported**\n\n` + `Configuration has been successfully imported and saved.` }] }; } /** * Handle the configuration wizard * @returns Promise with wizard interface content * @async */ async handleWizard(indicator) { // Get current configuration to show the user const config = this.configManager.getConfig(); const friendlyConfig = this.makeFriendlyConfig(config); // Build wizard content using templates // Note: Template builder is imported for future use when we fully migrate to template system const wizardContent = `${indicator}🧙 **Configuration Wizard - Let's Set Up DollhouseMCP!** I'll help you configure DollhouseMCP step by step. First, let me show you your current settings: **📊 Current Configuration:** \`\`\`yaml ${yaml.dump(friendlyConfig, { lineWidth: -1 })} \`\`\` **Now, let's configure your settings one by one!** 🎯 **Step 1: User Identity** This tags your creations so you can find them later. Everything is saved locally on your computer. - To set a username: Say "Set my username to [your-name]" - To stay anonymous: Say "I'll stay anonymous" - Current: ${friendlyConfig.user?.username || '(not set - anonymous mode)'} 🔐 **Step 2: GitHub Integration (Optional)** Connect to GitHub to share your creations and browse community content. - To connect GitHub: Say "Connect my GitHub account" - To skip: Say "Skip GitHub for now" - Current: ${friendlyConfig.github?.auth_token ? 'Connected' : '(not connected)'} 🔄 **Step 3: Portfolio Sync (Optional)** Automatically backup your creations to GitHub. - To enable: Say "Enable auto-sync" - To keep manual: Say "I'll sync manually" - Current: ${friendlyConfig.portfolio?.auto_sync ? 'Enabled' : 'Manual'} 🎨 **Step 4: Display Preferences** Customize how DollhouseMCP shows information. - To show active persona: Say "Show persona indicators" - To keep minimal: Say "Use minimal display" - Current: ${friendlyConfig.indicator?.enabled ? 'Enabled' : 'Minimal'} 💡 **Quick Setup**: Say "Configure the basics" to set just username and GitHub 📝 **Detailed Setup**: Say "Configure everything" to go through all options ⏭️ **Skip for Now**: Say "Skip wizard" to use anonymous mode ✨ You can always change these settings later by saying "Open configuration wizard"`; return { content: [{ type: "text", text: wizardContent }] }; } /** * Format a value for user-friendly display * Replaces null/undefined with helpful messages */ formatValue(value) { if (value === null || value === undefined) { return "(not set)"; } if (typeof value === 'string' && value.trim() === '') { return "(empty)"; } return JSON.stringify(value, null, 2); } /** * Make configuration display user-friendly for non-technical users * Replaces null values with helpful explanations * Uses centralized friendly values for i18n support */ makeFriendlyConfig(config) { const friendly = JSON.parse(JSON.stringify(config)); // Deep clone // Helper function to replace null values with friendly messages const replaceFriendlyValue = (obj, path = '') => { for (const key in obj) { const currentPath = path ? `${path}.${key}` : key; if (obj[key] === null) { obj[key] = getFriendlyNullValue(currentPath); } else if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { replaceFriendlyValue(obj[key], currentPath); } } }; // Apply friendly replacements replaceFriendlyValue(friendly); // Apply legacy manual replacements and defaults this.applyLegacyReplacements(friendly); return friendly; } /** * Apply legacy manual replacements for backward compatibility * Extracted to reduce cognitive complexity */ applyLegacyReplacements(friendly) { // Legacy manual replacements for backward compatibility // (These will be removed once we fully migrate to template system) if (friendly.sync) { if (friendly.sync.last_sync === "(not set)") { friendly.sync.last_sync = "(never synced)"; } if (friendly.sync.remote_url === "(not set)") { friendly.sync.remote_url = "(no remote repository)"; } } // Display settings if (friendly.display) { // These are typically booleans, but handle nulls just in case if (friendly.display.show_persona_indicator === null) { friendly.display.show_persona_indicator = true; // Default value } } // Collection settings if (friendly.collection) { if (friendly.collection.auto_submit === null) { friendly.collection.auto_submit = false; // Default value } if (friendly.collection.last_cache_update === null) { friendly.collection.last_cache_update = "(cache not initialized)"; } } // Wizard settings - show friendly status if (friendly.wizard) { friendly.wizard._status = this.getWizardStatusMessage(friendly.wizard); } } /** * Get friendly status message for wizard configuration * Extracted to reduce cognitive complexity */ getWizardStatusMessage(wizard) { if (wizard.completed === false && wizard.dismissed === false) { return "⏳ Ready to run (not completed)"; } if (wizard.completed === true) { return "✅ Completed"; } if (wizard.dismissed === true) { return "⏭️ Dismissed"; } return ""; } /** * Format source priority configuration for display */ formatSourcePriorityConfig(config, indicator) { const orderDisplay = config.priority .map(s => getSourceDisplayName(s)) .join(' → '); return `${indicator}🎯 **Source Priority Configuration** **Search Order**: ${orderDisplay} **Settings**: - **Stop on First Match**: ${config.stopOnFirst ? 'Yes' : 'No'} ${config.stopOnFirst ? 'Search stops as soon as an element is found' : 'All sources are searched'} - **Check All for Updates**: ${config.checkAllForUpdates ? 'Yes' : 'No'} ${config.checkAllForUpdates ? 'Always check all sources to find latest version' : 'Use first match for version'} - **Fallback on Error**: ${config.fallbackOnError ? 'Yes' : 'No'} ${config.fallbackOnError ? 'Try next source if current source fails' : 'Stop on any error'} **What This Means**: When searching for elements, the system will check sources in this order: ${config.priority.map((s, i) => `${i + 1}. ${getSourceDisplayName(s)}`).join('\n')} To change settings, use: \`dollhouse_config action: "set", setting: "source_priority.order", value: ["local", "github", "collection"]\` \`dollhouse_config action: "set", setting: "source_priority.stop_on_first", value: true\` \`dollhouse_config action: "set", setting: "source_priority.check_all_for_updates", value: false\` \`dollhouse_config action: "set", setting: "source_priority.fallback_on_error", value: true\``; } /** * Handle setting source priority configuration */ async handleSourcePrioritySet(options, indicator) { const setting = options.setting; let value = options.value; // Get current configuration const currentConfig = getSourcePriorityConfig(); try { // Normalize setting path const settingPath = setting.replace('source.priority', 'source_priority'); // Handle different source_priority settings if (settingPath === 'source_priority' || settingPath === 'source_priority.order') { // Parse and validate the new order const newOrder = parseSourcePriorityOrder(value); const newConfig = { ...currentConfig, priority: newOrder }; // Validate before saving const validation = validateSourcePriority(newConfig); if (!validation.isValid) { const errorList = validation.errors.map(e => `- ${e}`).join('\n'); return { content: [{ type: "text", text: `${indicator}❌ **Invalid Source Priority Configuration**\n\n` + `Errors:\n${errorList}\n\n` + `Valid sources: local, github, collection\n` + `Example: ["local", "github", "collection"]` }] }; } await saveSourcePriorityConfig(newConfig); return { content: [{ type: "text", text: `${indicator}✅ **Source Priority Order Updated**\n\n` + `New search order: ${newOrder.map(s => getSourceDisplayName(s)).join(' → ')}\n\n` + `Changes have been saved to the configuration file.` }] }; } // Handle boolean settings if (typeof value === 'string') { const lowerValue = value.toLowerCase(); if (lowerValue === 'true') value = true; else if (lowerValue === 'false') value = false; } if (typeof value !== 'boolean') { return { content: [{ type: "text", text: `${indicator}❌ **Invalid Value**\n\n` + `Setting '${setting}' requires a boolean value (true or false).\n` + `Received: ${JSON.stringify(value)}` }] }; } // Update specific setting const newConfig = { ...currentConfig }; if (settingPath === 'source_priority.stop_on_first' || settingPath === 'source_priority.stopOnFirst') { newConfig.stopOnFirst = value; } else if (settingPath === 'source_priority.check_all_for_updates' || settingPath === 'source_priority.checkAllForUpdates') { newConfig.checkAllForUpdates = value; } else if (settingPath === 'source_priority.fallback_on_error' || settingPath === 'source_priority.fallbackOnError') { newConfig.fallbackOnError = value; } else { return { content: [{ type: "text", text: `${indicator}❌ **Unknown Setting**\n\n` + `Unknown source priority setting: ${setting}\n\n` + `Valid settings:\n` + `- source_priority.order\n` + `- source_priority.stop_on_first\n` + `- source_priority.check_all_for_updates\n` + `- source_priority.fallback_on_error` }] }; } await saveSourcePriorityConfig(newConfig); return { content: [{ type: "text", text: `${indicator}✅ **Source Priority Setting Updated**\n\n` + `**${setting}** set to: ${value}\n\n` + `Changes have been saved to the configuration file.` }] }; } catch (error) { return { content: [{ type: "text", text: `${indicator}❌ **Configuration Update Failed**\n\n` + `Error: ${error instanceof Error ? error.message : String(error)}\n\n` + `Please check your input and try again.` }] }; } } /** * Handle setting indicator configuration with validation and immediate runtime update. * * This method provides unified immediate+persistent behavior: * 1. Validates the value against allowed options * 2. Saves to persistent config file * 3. Updates runtime PersonaIndicatorService for immediate effect * * @param options - Configuration options with setting path and value * @param indicator - Current indicator prefix for output formatting * @returns Promise with operation result */ async handleIndicatorSet(options, indicator) { const setting = options.setting; let value = options.value; // Get the indicator property name from the path const indicatorProp = INDICATOR_PATH_MAP[setting]; if (!indicatorProp) { return { content: [{ type: "text", text: `${indicator}❌ **Unknown Indicator Setting**\n\n` + `Unknown setting: ${setting}\n\n` + `Valid indicator settings:\n` + Object.keys(INDICATOR_PATH_MAP).map(p => `- ${p}`).join('\n') }] }; } // Type coercion for booleans if (typeof value === 'string') { const lowerValue = value.toLowerCase(); if (lowerValue === 'true') value = true; else if (lowerValue === 'false') value = false; } // Validate based on the property type const validationError = this.validateIndicatorValue(indicatorProp, value); if (validationError) { return { content: [{ type: "text", text: `${indicator}❌ **Invalid Value**\n\n${validationError}` }] }; } try { // 1. Save to persistent config await this.configManager.updateSetting(setting, value); // 2. Update runtime indicator service for immediate effect const currentConfig = this.indicatorService.getConfig(); const updatedConfig = { ...currentConfig, [indicatorProp]: value }; this.indicatorService.updateConfig(updatedConfig); // Get the new indicator to show the effect const newIndicator = this.indicatorService.getPersonaIndicator(); return { content: [{ type: "text", text: `${newIndicator}✅ **Indicator Configuration Updated**\n\n` + `**${setting}** set to: ${JSON.stringify(value)}\n\n` + `Changes are saved and take effect immediately.\n` + `Current indicator: ${newIndicator || '(disabled)'}` }] }; } catch (error) { return { content: [{ type: "text", text: `${indicator}❌ **Configuration Update Failed**\n\n` + `Error: ${error instanceof Error ? error.message : String(error)}` }] }; } } /** * Validate an indicator configuration value based on its property type. * * @param prop - The IndicatorConfig property name * @param value - The value to validate * @returns Error message if invalid, undefined if valid */ validateIndicatorValue(prop, value) { switch (prop) { case 'enabled': case 'showEmoji': case 'showName': case 'showVersion': case 'showAuthor': case 'showCategory': if (typeof value !== 'boolean') { return `Setting '${prop}' requires a boolean value (true or false).\nReceived: ${JSON.stringify(value)}`; } break; case 'style': if (!VALID_INDICATOR_STYLES.includes(value)) { return `Invalid style '${value}'.\nValid styles: ${VALID_INDICATOR_STYLES.join(', ')}`; } break; case 'bracketStyle': if (!VALID_BRACKET_STYLES.includes(value)) { return `Invalid bracket style '${value}'.\nValid styles: ${VALID_BRACKET_STYLES.join(', ')}`; } break; case 'customFormat': { if (typeof value !== 'string') { return `Custom format must be a string.\nReceived: ${typeof value}`; } const formatValidation = validateCustomFormat(value); if (!formatValidation.valid) { return formatValidation.error; } break; } case 'emoji': case 'separator': if (typeof value !== 'string') { return `Setting '${prop}' must be a string.\nReceived: ${typeof value}`; } break; } return undefined; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29uZmlnSGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oYW5kbGVycy9Db25maWdIYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUdILE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBR3BFLE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsd0JBQXdCLEVBQ3hCLHNCQUFzQixFQUN0Qix3QkFBd0IsRUFDeEIsb0JBQW9CLEVBQ3BCLHVCQUF1QixFQUV4QixNQUFNLDZCQUE2QixDQUFDO0FBQ3JDLE9BQU8sRUFDTCxvQkFBb0IsRUFFckIsTUFBTSwrQkFBK0IsQ0FBQztBQUN2QyxPQUFPLEVBQ0wsbUJBQW1CLEdBRXBCLE1BQU0sc0NBQXNDLENBQUM7QUFDOUMsT0FBTyxLQUFLLElBQUksTUFBTSxTQUFTLENBQUM7QUFFaEMsbUNBQW1DO0FBQ25DLE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxNQUFNLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxRQUFRLENBQVUsQ0FBQztBQUVqRixpQ0FBaUM7QUFDakMsTUFBTSxvQkFBb0IsR0FBRyxDQUFDLFFBQVEsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQVUsQ0FBQztBQUVwRix3RUFBd0U7QUFDeEUsTUFBTSxrQkFBa0IsR0FBMEM7SUFDaEUsMkJBQTJCLEVBQUUsU0FBUztJQUN0Qyx5QkFBeUIsRUFBRSxPQUFPO0lBQ2xDLGdDQUFnQyxFQUFFLGNBQWM7SUFDaEQsNkJBQTZCLEVBQUUsV0FBVztJQUMxQyw0QkFBNEIsRUFBRSxVQUFVO0lBQ3hDLCtCQUErQixFQUFFLGFBQWE7SUFDOUMsOEJBQThCLEVBQUUsWUFBWTtJQUM1QyxnQ0FBZ0MsRUFBRSxjQUFjO0lBQ2hELDZCQUE2QixFQUFFLFdBQVc7SUFDMUMs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