Skip to main content
Glama

meMCP - Memory-Enhanced Model Context Protocol

MIT License
23
2
ConfigurationManager.js9.53 kB
import { promises as fs } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; export class ConfigurationManager { constructor() { this.configDir = join(homedir(), '.mcp_sequential_thinking', 'config'); this.config = { factTypes: null, scoringWeights: null, settings: null, }; this.initialized = false; } async initialize() { try { await this.ensureConfigDirectory(); await this.loadConfigurations(); this.initialized = true; console.log('ConfigurationManager initialized'); } catch (error) { console.error('Failed to initialize ConfigurationManager:', error); throw error; } } async ensureConfigDirectory() { try { await fs.mkdir(this.configDir, { recursive: true }); await fs.mkdir(join(this.configDir, 'backups'), { recursive: true }); } catch (error) { console.error('Failed to create config directory:', error); throw error; } } async loadConfigurations() { await Promise.all([ this.loadFactTypes(), this.loadScoringWeights(), this.loadSettings(), ]); } async loadFactTypes() { try { const factTypesPath = join(this.configDir, 'fact-types.json'); const data = await fs.readFile(factTypesPath, 'utf-8'); this.config.factTypes = JSON.parse(data); } catch (error) { this.config.factTypes = null; } } async loadScoringWeights() { try { const weightsPath = join(this.configDir, 'scoring-weights.json'); const data = await fs.readFile(weightsPath, 'utf-8'); this.config.scoringWeights = JSON.parse(data); } catch (error) { this.config.scoringWeights = null; } } async loadSettings() { try { const settingsPath = join(this.configDir, 'settings.json'); const data = await fs.readFile(settingsPath, 'utf-8'); this.config.settings = JSON.parse(data); } catch (error) { this.config.settings = null; } } getFactTypes() { return this.config.factTypes; } async saveFactTypes(factTypes) { await this.backupConfig('fact-types.json'); const factTypesPath = join(this.configDir, 'fact-types.json'); await fs.writeFile(factTypesPath, JSON.stringify(factTypes, null, 2)); this.config.factTypes = factTypes; return true; } async ensureFactTypesConfig(defaultFactTypes) { if (!this.config.factTypes) { await this.saveFactTypes(defaultFactTypes); } } getScoringWeights() { return this.config.scoringWeights; } async saveScoringWeights(weights) { this.validateScoringWeights(weights); await this.backupConfig('scoring-weights.json'); const weightsPath = join(this.configDir, 'scoring-weights.json'); await fs.writeFile(weightsPath, JSON.stringify(weights, null, 2)); this.config.scoringWeights = weights; return true; } validateScoringWeights(weights) { const requiredDimensions = ['novelty', 'generalizability', 'specificity', 'validation', 'impact']; for (const dimension of requiredDimensions) { if (!(dimension in weights)) { throw new Error(`Missing required scoring dimension: ${dimension}`); } if (typeof weights[dimension] !== 'number' || weights[dimension] < 0 || weights[dimension] > 1) { throw new Error(`Invalid weight for ${dimension}: must be a number between 0 and 1`); } } const sum = Object.values(weights).reduce((total, weight) => total + weight, 0); if (Math.abs(sum - 1.0) > 0.001) { throw new Error(`Scoring weights must sum to 1.0, got ${sum}`); } } getSettings() { return this.config.settings || this.getDefaultSettings(); } getDefaultSettings() { return { qualityThreshold: 60, maxFactsPerSession: 100, retentionCheckInterval: 24 * 60 * 60 * 1000, autoBackup: true, backupRetention: 30, debugMode: false, enableHooks: true, processingDelay: 2000, }; } async saveSettings(settings) { await this.backupConfig('settings.json'); const settingsPath = join(this.configDir, 'settings.json'); await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2)); this.config.settings = settings; return true; } async updateSetting(key, value) { const currentSettings = this.getSettings(); const updatedSettings = { ...currentSettings, [key]: value, }; return await this.saveSettings(updatedSettings); } async backupConfig(filename) { try { const sourcePath = join(this.configDir, filename); const backupPath = join( this.configDir, 'backups', `${filename}.${Date.now()}.backup` ); const data = await fs.readFile(sourcePath, 'utf-8').catch(() => null); if (data) { await fs.writeFile(backupPath, data); } await this.cleanupOldBackups(filename); } catch (error) { console.warn(`Failed to backup ${filename}:`, error); } } async cleanupOldBackups(filename) { try { const backupsDir = join(this.configDir, 'backups'); const files = await fs.readdir(backupsDir); const backupFiles = files .filter(file => file.startsWith(filename) && file.endsWith('.backup')) .map(file => ({ name: file, path: join(backupsDir, file), timestamp: parseInt(file.split('.')[1], 10), })) .sort((a, b) => b.timestamp - a.timestamp); const settings = this.getSettings(); const maxBackups = settings.backupRetention || 30; if (backupFiles.length > maxBackups) { const filesToDelete = backupFiles.slice(maxBackups); for (const file of filesToDelete) { await fs.unlink(file.path); } } } catch (error) { console.warn('Failed to cleanup old backups:', error); } } async exportConfiguration() { const exportData = { factTypes: this.config.factTypes, scoringWeights: this.config.scoringWeights, settings: this.config.settings, exportedAt: new Date().toISOString(), version: '1.0.0', }; return JSON.stringify(exportData, null, 2); } async importConfiguration(configData) { let config; try { config = typeof configData === 'string' ? JSON.parse(configData) : configData; } catch (error) { throw new Error('Invalid configuration data: not valid JSON'); } if (config.factTypes) { await this.saveFactTypes(config.factTypes); } if (config.scoringWeights) { await this.saveScoringWeights(config.scoringWeights); } if (config.settings) { await this.saveSettings(config.settings); } return { imported: { factTypes: !!config.factTypes, scoringWeights: !!config.scoringWeights, settings: !!config.settings, }, timestamp: new Date().toISOString(), }; } async resetConfiguration(component = 'all') { const backupTimestamp = Date.now(); if (component === 'all' || component === 'factTypes') { await this.backupConfig('fact-types.json'); const factTypesPath = join(this.configDir, 'fact-types.json'); await fs.unlink(factTypesPath).catch(() => {}); this.config.factTypes = null; } if (component === 'all' || component === 'scoringWeights') { await this.backupConfig('scoring-weights.json'); const weightsPath = join(this.configDir, 'scoring-weights.json'); await fs.unlink(weightsPath).catch(() => {}); this.config.scoringWeights = null; } if (component === 'all' || component === 'settings') { await this.backupConfig('settings.json'); const settingsPath = join(this.configDir, 'settings.json'); await fs.unlink(settingsPath).catch(() => {}); this.config.settings = null; } return { reset: component, backedUp: true, timestamp: new Date().toISOString(), }; } async validateConfiguration() { const issues = []; if (this.config.scoringWeights) { try { this.validateScoringWeights(this.config.scoringWeights); } catch (error) { issues.push(`Scoring weights: ${error.message}`); } } if (this.config.factTypes) { for (const [typeName, typeConfig] of Object.entries(this.config.factTypes)) { if (!typeConfig.name || !typeConfig.description) { issues.push(`Fact type '${typeName}': missing name or description`); } if (typeof typeConfig.priority !== 'number' || typeConfig.priority < 1 || typeConfig.priority > 10) { issues.push(`Fact type '${typeName}': priority must be between 1 and 10`); } if (typeof typeConfig.retentionMonths !== 'number' || typeConfig.retentionMonths < 1) { issues.push(`Fact type '${typeName}': retentionMonths must be a positive number`); } } } return { valid: issues.length === 0, issues, checkedAt: new Date().toISOString(), }; } getConfigurationPaths() { return { configDir: this.configDir, factTypesPath: join(this.configDir, 'fact-types.json'), scoringWeightsPath: join(this.configDir, 'scoring-weights.json'), settingsPath: join(this.configDir, 'settings.json'), backupsDir: join(this.configDir, 'backups'), }; } }

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/mixelpixx/meMCP'

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