Skip to main content
Glama

MCP Index Notes

by vjsr007
config.ts12.6 kB
/** * Configuration system for MCP Index Notes * Provides flexible configuration management for server behavior, limits, and features */ import { z } from 'zod'; import { writeFileSync, readFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import { mkdirSync } from 'fs'; import { logger } from './logger.js'; // Configuration schemas export const DatabaseConfigSchema = z.object({ filePath: z.string().optional(), backupEnabled: z.boolean().default(true), backupInterval: z.number().min(0).default(3600000), // 1 hour in ms maxBackups: z.number().min(1).max(50).default(10), autoVacuum: z.boolean().default(true) }); export const SearchConfigSchema = z.object({ defaultLimit: z.number().min(1).max(1000).default(10), maxResults: z.number().min(1).max(10000).default(1000), enableFuzzySearch: z.boolean().default(true), fuzzyThreshold: z.number().min(0).max(1).default(0.6), highlightResults: z.boolean().default(true) }); export const AnalysisConfigSchema = z.object({ nlp: z.object({ enabled: z.boolean().default(true), minWordLength: z.number().min(1).max(10).default(3), maxKeywords: z.number().min(1).max(100).default(10), sentimentEnabled: z.boolean().default(true), entityExtractionEnabled: z.boolean().default(true) }), similarity: z.object({ defaultThreshold: z.number().min(0).max(1).default(0.3), maxComparisons: z.number().min(1).max(10000).default(1000), algorithm: z.enum(['jaccard', 'cosine', 'levenshtein']).default('jaccard') }), clustering: z.object({ enabled: z.boolean().default(true), defaultClusters: z.number().min(1).max(20).default(5), maxIterations: z.number().min(1).max(100).default(10), minClusterSize: z.number().min(1).max(100).default(2) }) }); export const StreamingConfigSchema = z.object({ enabled: z.boolean().default(true), defaultBatchSize: z.number().min(1).max(100).default(10), maxBatchSize: z.number().min(1).max(1000).default(100), defaultDelay: z.number().min(0).max(5000).default(50), maxItems: z.number().min(1).max(100000).default(10000), progressTracking: z.boolean().default(true) }); export const ServerConfigSchema = z.object({ maxConcurrentOperations: z.number().min(1).max(100).default(10), requestTimeout: z.number().min(1000).max(300000).default(30000), // 30 seconds enableCaching: z.boolean().default(true), cacheSize: z.number().min(10).max(10000).default(1000), rateLimiting: z.object({ enabled: z.boolean().default(false), requestsPerMinute: z.number().min(1).max(1000).default(100), burstLimit: z.number().min(1).max(100).default(20) }) }); export const ResourcesConfigSchema = z.object({ enabled: z.boolean().default(true), maxResponseSize: z.number().min(1024).max(10485760).default(1048576), // 1MB enableCompression: z.boolean().default(true), cacheTTL: z.number().min(0).max(3600000).default(300000) // 5 minutes }); export const PromptsConfigSchema = z.object({ enabled: z.boolean().default(true), customPrompts: z.array(z.object({ name: z.string(), description: z.string(), template: z.string(), arguments: z.array(z.object({ name: z.string(), description: z.string(), required: z.boolean().default(false) })).default([]) })).default([]), enableTemplateVariables: z.boolean().default(true) }); export const LoggingConfigSchema = z.object({ level: z.enum(['trace', 'debug', 'info', 'warn', 'error']).default('info'), pretty: z.boolean().default(false), enableMetrics: z.boolean().default(true), metricsInterval: z.number().min(1000).max(300000).default(60000), // 1 minute auditLog: z.boolean().default(false) }); // Main configuration schema export const ConfigSchema = z.object({ version: z.string().default('1.0.0'), database: DatabaseConfigSchema.optional(), search: SearchConfigSchema.optional(), analysis: AnalysisConfigSchema.optional(), streaming: StreamingConfigSchema.optional(), server: ServerConfigSchema.optional(), resources: ResourcesConfigSchema.optional(), prompts: PromptsConfigSchema.optional(), logging: LoggingConfigSchema.optional() }); export type Config = z.infer<typeof ConfigSchema>; export type DatabaseConfig = z.infer<typeof DatabaseConfigSchema>; export type SearchConfig = z.infer<typeof SearchConfigSchema>; export type AnalysisConfig = z.infer<typeof AnalysisConfigSchema>; export type StreamingConfig = z.infer<typeof StreamingConfigSchema>; export type ServerConfig = z.infer<typeof ServerConfigSchema>; export type ResourcesConfig = z.infer<typeof ResourcesConfigSchema>; export type PromptsConfig = z.infer<typeof PromptsConfigSchema>; export type LoggingConfig = z.infer<typeof LoggingConfigSchema>; /** * Configuration manager for the MCP server */ export class ConfigManager { private config: Config; private configPath: string; private watchers: Map<string, (newValue: any, oldValue: any) => void> = new Map(); constructor(configPath: string = './config.json') { this.configPath = configPath; this.config = this.loadConfig(); } /** * Load configuration from file or create default */ private loadConfig(): Config { try { if (existsSync(this.configPath)) { const configData = JSON.parse(readFileSync(this.configPath, 'utf-8')); const parsed = ConfigSchema.parse(configData); logger.info(`Configuration loaded successfully from ${this.configPath}`); return parsed; } else { // Create default configuration const defaultConfig = ConfigSchema.parse({}); this.saveConfig(defaultConfig); logger.info(`Default configuration created at ${this.configPath}`); return defaultConfig; } } catch (error: any) { logger.error(`Failed to load configuration, using defaults: ${error.message}`); return ConfigSchema.parse({}); } } /** * Save configuration to file */ private saveConfig(config: Config): void { try { // Ensure directory exists const dir = dirname(this.configPath); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8'); logger.info(`Configuration saved to ${this.configPath}`); } catch (error: any) { logger.error(`Failed to save configuration: ${error.message}`); } } /** * Get full configuration */ public getConfig(): Config { return { ...this.config }; } /** * Get section of configuration */ public getSection<K extends keyof Config>(section: K): Config[K] { return JSON.parse(JSON.stringify(this.config[section])); } /** * Update entire configuration */ public updateConfig(newConfig: Partial<Config>): boolean { try { const oldConfig = { ...this.config }; const mergedConfig = { ...this.config, ...newConfig }; const validated = ConfigSchema.parse(mergedConfig); this.config = validated; this.saveConfig(this.config); // Notify watchers this.notifyWatchers(oldConfig, this.config); return true; } catch (error: any) { logger.error(`Failed to update configuration: ${error.message}`); return false; } } /** * Update specific section of configuration */ public updateSection<K extends keyof Config>(section: K, sectionConfig: Partial<Config[K]>): boolean { try { const oldConfig = { ...this.config }; const currentSection = this.config[section] || {}; const updatedSection = { ...currentSection, ...sectionConfig }; return this.updateConfig({ [section]: updatedSection } as Partial<Config>); } catch (error: any) { logger.error(`Failed to update configuration section ${String(section)}: ${error.message}`); return false; } } /** * Reset configuration to defaults */ public resetConfig(): boolean { try { const defaultConfig = ConfigSchema.parse({}); this.config = defaultConfig; this.saveConfig(this.config); logger.info('Configuration reset to defaults'); return true; } catch (error: any) { logger.error(`Failed to reset configuration: ${error.message}`); return false; } } /** * Watch for configuration changes */ public watch(path: string, callback: (newValue: any, oldValue: any) => void): void { this.watchers.set(path, callback); } /** * Remove configuration watcher */ public unwatch(path: string): void { this.watchers.delete(path); } /** * Notify watchers of configuration changes */ private notifyWatchers(oldConfig: Config, newConfig: Config): void { for (const [path, callback] of this.watchers) { const oldValue = this.getValueByPath(oldConfig, path); const newValue = this.getValueByPath(newConfig, path); if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) { try { callback(newValue, oldValue); } catch (error: any) { logger.error(`Configuration watcher callback failed for ${path}: ${error.message}`); } } } } /** * Get configuration value by dot-notation path */ public getValue(path: string): any { return this.getValueByPath(this.config, path); } /** * Set configuration value by dot-notation path */ public setValue(path: string, value: any): boolean { try { const pathParts = path.split('.'); const newConfig = JSON.parse(JSON.stringify(this.config)); let current: any = newConfig; for (let i = 0; i < pathParts.length - 1; i++) { if (!(pathParts[i] in current)) { current[pathParts[i]] = {}; } current = current[pathParts[i]]; } current[pathParts[pathParts.length - 1]] = value; return this.updateConfig(newConfig); } catch (error: any) { logger.error(`Failed to set configuration value ${path}: ${error.message}`); return false; } } /** * Get value by dot-notation path */ private getValueByPath(obj: any, path: string): any { return path.split('.').reduce((current, key) => current?.[key], obj); } /** * Validate configuration schema */ public validateConfig(config: any): { valid: boolean; errors?: string[] } { try { ConfigSchema.parse(config); return { valid: true }; } catch (error) { if (error instanceof z.ZodError) { const errors = error.errors.map(err => `${err.path.join('.')}: ${err.message}`); return { valid: false, errors }; } return { valid: false, errors: [(error as Error).message] }; } } /** * Get configuration schema for documentation */ public getSchema(): any { return ConfigSchema; } /** * Export configuration with metadata */ public exportConfig(): { config: Config; metadata: { version: string; exportedAt: string; path: string } } { return { config: this.getConfig(), metadata: { version: this.config.version, exportedAt: new Date().toISOString(), path: this.configPath } }; } /** * Import configuration from exported data */ public importConfig(exportedData: { config: Config; metadata?: any }): boolean { try { const validated = ConfigSchema.parse(exportedData.config); this.config = validated; this.saveConfig(this.config); logger.info('Configuration imported successfully'); return true; } catch (error: any) { logger.error(`Failed to import configuration: ${error.message}`); return false; } } } // Global configuration instance let globalConfig: ConfigManager | null = null; /** * Get or create global configuration manager */ export function getConfig(configPath?: string): ConfigManager { if (!globalConfig) { globalConfig = new ConfigManager(configPath); } return globalConfig; } /** * Configuration validation schemas for API endpoints */ export const ConfigUpdateSchema = z.object({ section: z.enum(['database', 'search', 'analysis', 'streaming', 'server', 'resources', 'prompts', 'logging']).optional(), path: z.string().optional(), value: z.any(), merge: z.boolean().default(true) }); export const ConfigExportSchema = z.object({ includeMetadata: z.boolean().default(true), sections: z.array(z.string()).optional() }); export const ConfigImportSchema = z.object({ config: z.any(), overwrite: z.boolean().default(false), validateOnly: z.boolean().default(false) });

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/vjsr007/mcp-index-notes'

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