Skip to main content
Glama

Visa Design System MCP Server

by MarySuneela
index.ts11.7 kB
/** * Configuration system for Visa Design System MCP Server * Supports environment variables, default values, and validation */ import { join } from 'path'; import { readFile, access } from 'fs/promises'; export interface ServerConfig { /** Server name for MCP identification */ name: string; /** Server version */ version: string; /** Port for HTTP server (if enabled) */ port: number; /** Enable HTTP server alongside stdio */ enableHttp: boolean; /** Log level for server operations */ logLevel: 'error' | 'warn' | 'info' | 'debug'; /** Enable detailed request/response logging */ enableRequestLogging: boolean; } export interface DataConfig { /** Path to design system data files */ dataPath: string; /** Enable file watching for automatic updates */ enableFileWatching: boolean; /** Cache timeout in milliseconds */ cacheTimeout: number; /** Maximum cache size in MB */ maxCacheSize: number; /** Enable data validation on load */ enableValidation: boolean; } export interface SecurityConfig { /** Enable authentication (future feature) */ enableAuth: boolean; /** API key for authentication (if enabled) */ apiKey?: string; /** Allowed origins for CORS (if HTTP enabled) */ allowedOrigins: string[]; /** Rate limiting - requests per minute */ rateLimitRpm: number; } export interface PerformanceConfig { /** Maximum concurrent requests */ maxConcurrentRequests: number; /** Request timeout in milliseconds */ requestTimeout: number; /** Enable response compression */ enableCompression: boolean; /** Memory usage warning threshold in MB */ memoryWarningThreshold: number; } export interface MCPServerConfiguration { server: ServerConfig; data: DataConfig; security: SecurityConfig; performance: PerformanceConfig; } /** * Default configuration values */ const DEFAULT_CONFIG: MCPServerConfiguration = { server: { name: 'visa-design-system-mcp', version: '1.0.0', port: 3000, enableHttp: false, logLevel: 'info', enableRequestLogging: false }, data: { dataPath: join(process.cwd(), 'data'), enableFileWatching: true, cacheTimeout: 300000, // 5 minutes maxCacheSize: 100, // 100 MB enableValidation: true }, security: { enableAuth: false, allowedOrigins: ['*'], rateLimitRpm: 1000 }, performance: { maxConcurrentRequests: 100, requestTimeout: 30000, // 30 seconds enableCompression: true, memoryWarningThreshold: 512 // 512 MB } }; /** * Environment variable mappings */ const ENV_MAPPINGS = { // Server configuration 'MCP_SERVER_NAME': 'server.name', 'MCP_SERVER_VERSION': 'server.version', 'MCP_SERVER_PORT': 'server.port', 'MCP_ENABLE_HTTP': 'server.enableHttp', 'MCP_LOG_LEVEL': 'server.logLevel', 'MCP_ENABLE_REQUEST_LOGGING': 'server.enableRequestLogging', // Data configuration 'MCP_DATA_PATH': 'data.dataPath', 'MCP_ENABLE_FILE_WATCHING': 'data.enableFileWatching', 'MCP_CACHE_TIMEOUT': 'data.cacheTimeout', 'MCP_MAX_CACHE_SIZE': 'data.maxCacheSize', 'MCP_ENABLE_VALIDATION': 'data.enableValidation', // Security configuration 'MCP_ENABLE_AUTH': 'security.enableAuth', 'MCP_API_KEY': 'security.apiKey', 'MCP_ALLOWED_ORIGINS': 'security.allowedOrigins', 'MCP_RATE_LIMIT_RPM': 'security.rateLimitRpm', // Performance configuration 'MCP_MAX_CONCURRENT_REQUESTS': 'performance.maxConcurrentRequests', 'MCP_REQUEST_TIMEOUT': 'performance.requestTimeout', 'MCP_ENABLE_COMPRESSION': 'performance.enableCompression', 'MCP_MEMORY_WARNING_THRESHOLD': 'performance.memoryWarningThreshold' } as const; export interface ConfigValidationResult { valid: boolean; errors?: string[]; warnings?: string[]; } /** * Configuration loader and validator */ export class ConfigurationManager { private config: MCPServerConfiguration; private configFilePath?: string; constructor(configFilePath?: string) { this.config = this.deepClone(DEFAULT_CONFIG); this.configFilePath = configFilePath; } /** * Load configuration from environment variables and optional config file */ async loadConfiguration(): Promise<ConfigValidationResult> { const errors: string[] = []; const warnings: string[] = []; try { // Load from config file if specified if (this.configFilePath) { const fileResult = await this.loadFromFile(this.configFilePath); if (!fileResult.success && fileResult.errors) { errors.push(...fileResult.errors); } else if (fileResult.warnings) { warnings.push(...fileResult.warnings); } } // Load from environment variables (overrides file config) this.loadFromEnvironment(); // Validate the final configuration const validation = this.validateConfiguration(); if (!validation.valid && validation.errors) { errors.push(...validation.errors); } if (validation.warnings) { warnings.push(...validation.warnings); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, warnings: warnings.length > 0 ? warnings : undefined }; } catch (error) { return { valid: false, errors: [`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`] }; } } /** * Get the current configuration */ getConfiguration(): MCPServerConfiguration { return this.deepClone(this.config); } /** * Get a specific configuration section */ getServerConfig(): ServerConfig { return { ...this.config.server }; } getDataConfig(): DataConfig { return { ...this.config.data }; } getSecurityConfig(): SecurityConfig { return { ...this.config.security }; } getPerformanceConfig(): PerformanceConfig { return { ...this.config.performance }; } /** * Load configuration from a JSON file */ private async loadFromFile(filePath: string): Promise<{ success: boolean; errors?: string[]; warnings?: string[] }> { try { // Check if file exists try { await access(filePath); } catch { return { success: false, warnings: [`Configuration file not found: ${filePath}, using defaults`] }; } const content = await readFile(filePath, 'utf-8'); const fileConfig = JSON.parse(content); // Merge with current config this.config = this.deepMerge(this.config, fileConfig); return { success: true }; } catch (error) { return { success: false, errors: [`Failed to load config file: ${error instanceof Error ? error.message : 'Unknown error'}`] }; } } /** * Load configuration from environment variables */ private loadFromEnvironment(): void { for (const [envVar, configPath] of Object.entries(ENV_MAPPINGS)) { const value = process.env[envVar]; if (value !== undefined) { this.setNestedValue(this.config, configPath, this.parseEnvValue(value)); } } } /** * Parse environment variable value to appropriate type */ private parseEnvValue(value: string): any { // Boolean values if (value.toLowerCase() === 'true') return true; if (value.toLowerCase() === 'false') return false; // Number values if (/^\d+$/.test(value)) return parseInt(value, 10); if (/^\d+\.\d+$/.test(value)) return parseFloat(value); // Array values (comma-separated) if (value.includes(',')) { return value.split(',').map(item => item.trim()); } // String values return value; } /** * Set nested object value using dot notation */ private setNestedValue(obj: any, path: string, value: any): void { const keys = path.split('.'); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!(key in current) || typeof current[key] !== 'object') { current[key] = {}; } current = current[key]; } current[keys[keys.length - 1]] = value; } /** * Validate the configuration */ private validateConfiguration(): ConfigValidationResult { const errors: string[] = []; const warnings: string[] = []; // Validate server configuration if (!this.config.server.name || this.config.server.name.trim() === '') { errors.push('Server name cannot be empty'); } if (!this.config.server.version || this.config.server.version.trim() === '') { errors.push('Server version cannot be empty'); } if (this.config.server.port < 1 || this.config.server.port > 65535) { errors.push('Server port must be between 1 and 65535'); } if (!['error', 'warn', 'info', 'debug'].includes(this.config.server.logLevel)) { errors.push('Log level must be one of: error, warn, info, debug'); } // Validate data configuration if (!this.config.data.dataPath || this.config.data.dataPath.trim() === '') { errors.push('Data path cannot be empty'); } if (this.config.data.cacheTimeout < 0) { errors.push('Cache timeout cannot be negative'); } if (this.config.data.maxCacheSize < 1) { errors.push('Max cache size must be at least 1 MB'); } // Validate security configuration if (this.config.security.rateLimitRpm < 1) { errors.push('Rate limit must be at least 1 request per minute'); } if (this.config.security.enableAuth && !this.config.security.apiKey) { warnings.push('Authentication is enabled but no API key is configured'); } // Validate performance configuration if (this.config.performance.maxConcurrentRequests < 1) { errors.push('Max concurrent requests must be at least 1'); } if (this.config.performance.requestTimeout < 1000) { warnings.push('Request timeout is very low (< 1 second), this may cause issues'); } if (this.config.performance.memoryWarningThreshold < 64) { warnings.push('Memory warning threshold is very low (< 64 MB)'); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, warnings: warnings.length > 0 ? warnings : undefined }; } /** * Deep clone an object */ private deepClone<T>(obj: T): T { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()) as any; if (Array.isArray(obj)) return obj.map(item => this.deepClone(item)) as any; const cloned = {} as T; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = this.deepClone(obj[key]); } } return cloned; } /** * Deep merge two objects */ private deepMerge<T>(target: T, source: any): T { const result = this.deepClone(target); for (const key in source) { if (source.hasOwnProperty(key)) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { (result as any)[key] = this.deepMerge((result as any)[key] || {}, source[key]); } else { (result as any)[key] = source[key]; } } } return result; } } /** * Create and load configuration */ export async function loadConfiguration(configFilePath?: string): Promise<{ config: MCPServerConfiguration; validation: ConfigValidationResult; }> { const manager = new ConfigurationManager(configFilePath); const validation = await manager.loadConfiguration(); const config = manager.getConfiguration(); return { config, validation }; } /** * Export default configuration for testing */ export { DEFAULT_CONFIG };

Latest Blog Posts

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/MarySuneela/mcp-vpds'

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