Skip to main content
Glama

Claude Desktop Commander MCP

feature-flags.ts4.76 kB
import fs from 'fs/promises'; import path from 'path'; import { existsSync } from 'fs'; import { CONFIG_FILE } from '../config.js'; import { logger } from './logger.js'; interface FeatureFlags { version?: string; flags?: Record<string, any>; } class FeatureFlagManager { private flags: Record<string, any> = {}; private lastFetch: number = 0; private cachePath: string; private cacheMaxAge: number = 30 * 60 * 1000; private flagUrl: string; private refreshInterval: NodeJS.Timeout | null = null; constructor() { const configDir = path.dirname(CONFIG_FILE); this.cachePath = path.join(configDir, 'feature-flags.json'); // Use production flags this.flagUrl = process.env.DC_FLAG_URL || 'https://desktopcommander.app/flags/v1/production.json'; } /** * Initialize - load from cache and start background refresh */ async initialize(): Promise<void> { try { // Load from cache immediately (non-blocking) await this.loadFromCache(); // Fetch in background (don't block startup) this.fetchFlags().catch(err => { logger.debug('Initial flag fetch failed:', err.message); }); // Start periodic refresh every 5 minutes this.refreshInterval = setInterval(() => { this.fetchFlags().catch(err => { logger.debug('Periodic flag fetch failed:', err.message); }); }, this.cacheMaxAge); logger.info(`Feature flags initialized (refresh every ${this.cacheMaxAge / 1000}s)`); } catch (error) { logger.warning('Failed to initialize feature flags:', error); } } /** * Get a flag value */ get(flagName: string, defaultValue: any = false): any { return this.flags[flagName] !== undefined ? this.flags[flagName] : defaultValue; } /** * Get all flags for debugging */ getAll(): Record<string, any> { return { ...this.flags }; } /** * Manually refresh flags immediately (for testing) */ async refresh(): Promise<boolean> { try { await this.fetchFlags(); return true; } catch (error) { logger.error('Manual refresh failed:', error); return false; } } /** * Load flags from local cache */ private async loadFromCache(): Promise<void> { try { if (!existsSync(this.cachePath)) { logger.debug('No feature flag cache found'); return; } const data = await fs.readFile(this.cachePath, 'utf8'); const config: FeatureFlags = JSON.parse(data); if (config.flags) { this.flags = config.flags; this.lastFetch = Date.now(); logger.debug(`Loaded ${Object.keys(this.flags).length} feature flags from cache`); } } catch (error) { logger.warning('Failed to load feature flags from cache:', error); } } /** * Fetch flags from remote URL */ private async fetchFlags(): Promise<void> { try { logger.debug('Fetching feature flags from:', this.flagUrl); const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); const response = await fetch(this.flagUrl, { signal: controller.signal, headers: { 'Cache-Control': 'no-cache', } }); clearTimeout(timeout); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const config: FeatureFlags = await response.json(); // Update flags if (config.flags) { this.flags = config.flags; this.lastFetch = Date.now(); // Save to cache await this.saveToCache(config); logger.info(`Feature flags updated: ${Object.keys(this.flags).length} flags`); } } catch (error: any) { logger.debug('Failed to fetch feature flags:', error.message); // Continue with cached values } } /** * Save flags to local cache */ private async saveToCache(config: FeatureFlags): Promise<void> { try { const configDir = path.dirname(this.cachePath); if (!existsSync(configDir)) { await fs.mkdir(configDir, { recursive: true }); } await fs.writeFile(this.cachePath, JSON.stringify(config, null, 2), 'utf8'); logger.debug('Saved feature flags to cache'); } catch (error) { logger.warning('Failed to save feature flags to cache:', error); } } /** * Cleanup on shutdown */ destroy(): void { if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = null; } } } // Export singleton instance export const featureFlagManager = new FeatureFlagManager();

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/wonderwhy-er/DesktopCommanderMCP'

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