Skip to main content
Glama

hypertool-mcp

extensionConfig.tsโ€ข7.99 kB
/** * Extension Configuration Management * Handles loading, saving, and validation of extension configurations */ import { readFile, writeFile, mkdir } from "fs/promises"; import { join, dirname } from "path"; import { homedir } from "os"; import { existsSync } from "fs"; import { HypertoolConfig, ExtensionConfig, ExtensionUserConfig, } from "./dxt-config.js"; import { isDxtEnabledViaService } from "./featureFlagService.js"; /** * Default extension configuration */ const DEFAULT_EXTENSION_CONFIG: ExtensionConfig = { directory: join(homedir(), ".toolprint", "hypertool-mcp", "extensions"), autoDiscovery: true, settings: {}, }; /** * Extension configuration manager */ export class ExtensionConfigManager { private readonly configPath: string; private config: HypertoolConfig = {}; constructor(configPath?: string) { const base = configPath || join(homedir(), ".toolprint", "hypertool-mcp", "config.json"); this.configPath = base; } /** * Load configuration from file */ async load(): Promise<HypertoolConfig> { // If DXT is disabled, return empty config and skip file operations if (!(await isDxtEnabledViaService())) { this.config = { extensions: { directory: "", autoDiscovery: false, settings: {} }, }; return this.config; } try { if (existsSync(this.configPath)) { const content = await readFile(this.configPath, "utf-8"); this.config = JSON.parse(content); } else { // Initialize with defaults this.config = { extensions: DEFAULT_EXTENSION_CONFIG, }; } // Ensure extensions config exists if (!this.config.extensions) { this.config.extensions = DEFAULT_EXTENSION_CONFIG; } // Ensure required fields exist if (!this.config.extensions.settings) { this.config.extensions.settings = {}; } if (this.config.extensions.autoDiscovery === undefined) { this.config.extensions.autoDiscovery = true; } if (!this.config.extensions.directory) { this.config.extensions.directory = DEFAULT_EXTENSION_CONFIG.directory; } return this.config; } catch (error) { console.error( `Failed to load extension config: ${(error as Error).message}` ); return this.getDefaultConfig(); } } /** * Save configuration to file */ async save(config?: HypertoolConfig): Promise<void> { const configToSave = config || this.config; try { // Ensure directory exists await mkdir(dirname(this.configPath), { recursive: true }); // Save with pretty formatting await writeFile(this.configPath, JSON.stringify(configToSave, null, 2)); this.config = configToSave; } catch (error) { throw new Error( `Failed to save extension config: ${(error as Error).message}` ); } } /** * Get current configuration */ getConfig(): HypertoolConfig { return { ...this.config }; } /** * Get extension settings for a specific extension */ getExtensionSettings(extensionName: string): ExtensionUserConfig | undefined { return this.config.extensions?.settings?.[extensionName]; } /** * Set extension settings for a specific extension */ async setExtensionSettings( extensionName: string, settings: ExtensionUserConfig ): Promise<void> { if (!this.config.extensions) { this.config.extensions = DEFAULT_EXTENSION_CONFIG; } if (!this.config.extensions.settings) { this.config.extensions.settings = {}; } this.config.extensions.settings[extensionName] = settings; await this.save(); } /** * Enable an extension */ async enableExtension(extensionName: string): Promise<void> { const currentSettings = this.getExtensionSettings(extensionName); await this.setExtensionSettings(extensionName, { ...currentSettings, isEnabled: true, }); } /** * Disable an extension */ async disableExtension(extensionName: string): Promise<void> { const currentSettings = this.getExtensionSettings(extensionName); await this.setExtensionSettings(extensionName, { ...currentSettings, isEnabled: false, }); } /** * Update user config for an extension */ async updateExtensionUserConfig( extensionName: string, userConfig: Record<string, any> ): Promise<void> { const currentSettings = this.getExtensionSettings(extensionName) || { isEnabled: true, }; await this.setExtensionSettings(extensionName, { ...currentSettings, userConfig, }); } /** * Remove extension settings */ async removeExtension(extensionName: string): Promise<void> { if (this.config.extensions?.settings) { delete this.config.extensions.settings[extensionName]; await this.save(); } } /** * Get all extension names */ getExtensionNames(): string[] { return Object.keys(this.config.extensions?.settings || {}); } /** * Check if auto-discovery is enabled */ isAutoDiscoveryEnabled(): boolean { return this.config.extensions?.autoDiscovery ?? true; } /** * Set auto-discovery setting */ async setAutoDiscovery(enabled: boolean): Promise<void> { if (!this.config.extensions) { this.config.extensions = DEFAULT_EXTENSION_CONFIG; } this.config.extensions.autoDiscovery = enabled; await this.save(); } /** * Get extensions directory */ getExtensionsDirectory(): string { return ( this.config.extensions?.directory || DEFAULT_EXTENSION_CONFIG.directory! ); } /** * Set extensions directory */ async setExtensionsDirectory(directory: string): Promise<void> { if (!this.config.extensions) { this.config.extensions = DEFAULT_EXTENSION_CONFIG; } this.config.extensions.directory = directory; await this.save(); } /** * Get default configuration */ private getDefaultConfig(): HypertoolConfig { return { extensions: DEFAULT_EXTENSION_CONFIG, }; } /** * Validate configuration structure */ validateConfig(config: any): { isValid: boolean; errors: string[] } { const errors: string[] = []; if (config.extensions) { const ext = config.extensions; if (ext.directory && typeof ext.directory !== "string") { errors.push("extensions.directory must be a string"); } if ( ext.autoDiscovery !== undefined && typeof ext.autoDiscovery !== "boolean" ) { errors.push("extensions.autoDiscovery must be a boolean"); } if (ext.settings) { if (typeof ext.settings !== "object" || Array.isArray(ext.settings)) { errors.push("extensions.settings must be an object"); } else { for (const [name, settings] of Object.entries(ext.settings)) { if (typeof settings !== "object" || Array.isArray(settings)) { errors.push(`extensions.settings.${name} must be an object`); continue; } const s = settings as any; if (s.isEnabled !== undefined && typeof s.isEnabled !== "boolean") { errors.push( `extensions.settings.${name}.isEnabled must be a boolean` ); } if ( s.userConfig !== undefined && (typeof s.userConfig !== "object" || Array.isArray(s.userConfig)) ) { errors.push( `extensions.settings.${name}.userConfig must be an object` ); } } } } } return { isValid: errors.length === 0, errors, }; } /** * Initialize configuration file if it doesn't exist */ async initialize(): Promise<void> { if (!existsSync(this.configPath)) { await this.save(this.getDefaultConfig()); } } }

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/toolprint/hypertool-mcp'

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