Skip to main content
Glama
config.ts13.1 kB
import { z } from 'zod'; import { config as dotenvConfig } from 'dotenv'; // Load environment variables dotenvConfig(); // Environment validation schema const envSchema = z.object({ // Environment NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), // Server configuration PORT: z.string().regex(/^\d+$/).transform(Number).default('3000'), HOST: z.string().default('0.0.0.0'), // Database configuration DATABASE_URL: z.string().url(), DATABASE_SSL: z.string().transform(val => val === 'true').default('false'), DATABASE_POOL_MIN: z.string().regex(/^\d+$/).transform(Number).default('2'), DATABASE_POOL_MAX: z.string().regex(/^\d+$/).transform(Number).default('20'), DATABASE_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('30000'), // Redis configuration REDIS_URL: z.string().url(), REDIS_PASSWORD: z.string().optional(), REDIS_DB: z.string().regex(/^\d+$/).transform(Number).default('0'), REDIS_CONNECT_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('10000'), REDIS_COMMAND_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('5000'), REDIS_RETRY_DELAY_ON_FAILURE: z.string().regex(/^\d+$/).transform(Number).default('100'), REDIS_MAX_RETRY_DELAY: z.string().regex(/^\d+$/).transform(Number).default('2000'), // Vault configuration VAULT_URL: z.string().url(), VAULT_TOKEN: z.string().min(1), VAULT_NAMESPACE: z.string().optional(), VAULT_ROLE_ID: z.string().optional(), VAULT_SECRET_ID: z.string().optional(), VAULT_KV_MOUNT: z.string().default('secret'), VAULT_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('10000'), // JWT configuration JWT_SECRET: z.string().min(32), JWT_ACCESS_EXPIRES_IN: z.string().default('15m'), JWT_REFRESH_EXPIRES_IN: z.string().default('7d'), JWT_ISSUER: z.string().default('secure-mcp-server'), JWT_AUDIENCE: z.string().default('secure-mcp-client'), // MFA configuration MFA_ISSUER: z.string().default('Secure MCP Server'), MFA_WINDOW: z.string().regex(/^\d+$/).transform(Number).default('1'), // SSO configuration (SAML) SAML_ENABLED: z.string().transform(val => val === 'true').default('false'), SAML_ENTRY_POINT: z.string().url().optional(), SAML_ISSUER: z.string().optional(), SAML_CALLBACK_URL: z.string().url().optional(), SAML_CERT: z.string().optional(), SAML_PRIVATE_KEY: z.string().optional(), // Rate limiting RATE_LIMIT_WINDOW_MS: z.string().regex(/^\d+$/).transform(Number).default('900000'), // 15 minutes RATE_LIMIT_MAX_REQUESTS: z.string().regex(/^\d+$/).transform(Number).default('100'), RATE_LIMIT_SKIP_SUCCESSFUL: z.string().transform(val => val === 'true').default('false'), RATE_LIMIT_SKIP_FAILED: z.string().transform(val => val === 'true').default('false'), // CORS configuration CORS_ORIGINS: z.string().transform(val => val.split(',')).default('http://localhost:3000'), CORS_CREDENTIALS: z.string().transform(val => val === 'true').default('true'), CORS_MAX_AGE: z.string().regex(/^\d+$/).transform(Number).default('86400'), // Security headers SECURITY_FORCE_HTTPS: z.string().transform(val => val === 'true').default('true'), SECURITY_HSTS_MAX_AGE: z.string().regex(/^\d+$/).transform(Number).default('31536000'), SECURITY_FRAME_OPTIONS: z.enum(['DENY', 'SAMEORIGIN']).default('DENY'), // Logging configuration LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug', 'trace']).default('info'), LOG_FORMAT: z.enum(['json', 'pretty']).default('json'), LOG_REDACT_SENSITIVE: z.string().transform(val => val === 'true').default('true'), // Monitoring METRICS_ENABLED: z.string().transform(val => val === 'true').default('true'), METRICS_PORT: z.string().regex(/^\d+$/).transform(Number).default('9090'), METRICS_PATH: z.string().default('/metrics'), TRACING_ENABLED: z.string().transform(val => val === 'true').default('true'), TRACING_ENDPOINT: z.string().url().optional(), TRACING_SERVICE_NAME: z.string().default('secure-mcp-server'), // WebSocket configuration WS_PING_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('60000'), WS_PING_INTERVAL: z.string().regex(/^\d+$/).transform(Number).default('25000'), WS_MAX_CONNECTIONS: z.string().regex(/^\d+$/).transform(Number).default('1000'), WS_MAX_MESSAGE_SIZE: z.string().regex(/^\d+$/).transform(Number).default('1048576'), // 1MB // MCP Protocol configuration MCP_PROTOCOL_VERSION: z.string().default('2024-11-05'), MCP_MAX_TOOLS: z.string().regex(/^\d+$/).transform(Number).default('100'), MCP_MAX_RESOURCES: z.string().regex(/^\d+$/).transform(Number).default('1000'), MCP_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('30000'), MCP_RATE_LIMIT_PER_CONNECTION: z.string().regex(/^\d+$/).transform(Number).default('100'), // Clustering configuration CLUSTERING_ENABLED: z.string().transform(val => val === 'true').default('false'), CLUSTERING_WORKERS: z.string().default('auto'), // Health check configuration HEALTH_CHECK_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('5000'), HEALTH_CHECK_INTERVAL: z.string().regex(/^\d+$/).transform(Number).default('30000'), // Encryption configuration ENCRYPTION_ALGORITHM: z.string().default('aes-256-gcm'), ENCRYPTION_KEY_DERIVATION: z.string().default('pbkdf2'), ENCRYPTION_ITERATIONS: z.string().regex(/^\d+$/).transform(Number).default('100000'), // Session configuration SESSION_SECRET: z.string().min(32), SESSION_TIMEOUT: z.string().regex(/^\d+$/).transform(Number).default('3600000'), // 1 hour SESSION_COOKIE_SECURE: z.string().transform(val => val === 'true').default('true'), SESSION_COOKIE_HTTP_ONLY: z.string().transform(val => val === 'true').default('true'), SESSION_COOKIE_SAME_SITE: z.enum(['strict', 'lax', 'none']).default('strict'), }); // Parse and validate environment variables const parseEnv = () => { try { return envSchema.parse(process.env); } catch (error) { if (error instanceof z.ZodError) { const missingVars = error.errors.map(err => `${err.path.join('.')}: ${err.message}`); throw new Error(`Invalid environment configuration:\n${missingVars.join('\n')}`); } throw error; } }; const env = parseEnv(); // Application configuration interface export interface AppConfig { env: string; server: { port: number; host: string; }; database: { url: string; ssl: boolean; pool: { min: number; max: number; }; timeout: number; }; redis: { url: string; password?: string; db: number; connectTimeout: number; commandTimeout: number; retryDelayOnFailure: number; maxRetryDelay: number; }; vault: { url: string; token: string; namespace?: string; roleId?: string; secretId?: string; kvMount: string; timeout: number; }; jwt: { secret: string; accessExpiresIn: string; refreshExpiresIn: string; issuer: string; audience: string; }; mfa: { issuer: string; window: number; }; saml: { enabled: boolean; entryPoint?: string; issuer?: string; callbackUrl?: string; cert?: string; privateKey?: string; }; rateLimit: { windowMs: number; maxRequests: number; skipSuccessfulRequests: boolean; skipFailedRequests: boolean; }; cors: { origins: string[]; credentials: boolean; maxAge: number; }; security: { forceHttps: boolean; hstsMaxAge: number; frameOptions: string; }; logging: { level: string; format: string; redactSensitive: boolean; }; monitoring: { enabled: boolean; port: number; path: string; }; tracing: { enabled: boolean; endpoint?: string; serviceName: string; }; websocket: { pingTimeout: number; pingInterval: number; maxConnections: number; maxMessageSize: number; }; mcp: { protocolVersion: string; maxTools: number; maxResources: number; timeout: number; rateLimitPerConnection: number; }; clustering: { enabled: boolean; workers: number | 'auto'; }; healthCheck: { timeout: number; interval: number; }; encryption: { algorithm: string; keyDerivation: string; iterations: number; }; session: { secret: string; timeout: number; cookie: { secure: boolean; httpOnly: boolean; sameSite: 'strict' | 'lax' | 'none'; }; }; } // Export validated configuration export const config: AppConfig = { env: env.NODE_ENV, server: { port: env.PORT, host: env.HOST, }, database: { url: env.DATABASE_URL, ssl: env.DATABASE_SSL, pool: { min: env.DATABASE_POOL_MIN, max: env.DATABASE_POOL_MAX, }, timeout: env.DATABASE_TIMEOUT, }, redis: { url: env.REDIS_URL, password: env.REDIS_PASSWORD, db: env.REDIS_DB, connectTimeout: env.REDIS_CONNECT_TIMEOUT, commandTimeout: env.REDIS_COMMAND_TIMEOUT, retryDelayOnFailure: env.REDIS_RETRY_DELAY_ON_FAILURE, maxRetryDelay: env.REDIS_MAX_RETRY_DELAY, }, vault: { url: env.VAULT_URL, token: env.VAULT_TOKEN, namespace: env.VAULT_NAMESPACE, roleId: env.VAULT_ROLE_ID, secretId: env.VAULT_SECRET_ID, kvMount: env.VAULT_KV_MOUNT, timeout: env.VAULT_TIMEOUT, }, jwt: { secret: env.JWT_SECRET, accessExpiresIn: env.JWT_ACCESS_EXPIRES_IN, refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN, issuer: env.JWT_ISSUER, audience: env.JWT_AUDIENCE, }, mfa: { issuer: env.MFA_ISSUER, window: env.MFA_WINDOW, }, saml: { enabled: env.SAML_ENABLED, entryPoint: env.SAML_ENTRY_POINT, issuer: env.SAML_ISSUER, callbackUrl: env.SAML_CALLBACK_URL, cert: env.SAML_CERT, privateKey: env.SAML_PRIVATE_KEY, }, rateLimit: { windowMs: env.RATE_LIMIT_WINDOW_MS, maxRequests: env.RATE_LIMIT_MAX_REQUESTS, skipSuccessfulRequests: env.RATE_LIMIT_SKIP_SUCCESSFUL, skipFailedRequests: env.RATE_LIMIT_SKIP_FAILED, }, cors: { origins: env.CORS_ORIGINS, credentials: env.CORS_CREDENTIALS, maxAge: env.CORS_MAX_AGE, }, security: { forceHttps: env.SECURITY_FORCE_HTTPS, hstsMaxAge: env.SECURITY_HSTS_MAX_AGE, frameOptions: env.SECURITY_FRAME_OPTIONS, }, logging: { level: env.LOG_LEVEL, format: env.LOG_FORMAT, redactSensitive: env.LOG_REDACT_SENSITIVE, }, monitoring: { enabled: env.METRICS_ENABLED, port: env.METRICS_PORT, path: env.METRICS_PATH, }, tracing: { enabled: env.TRACING_ENABLED, endpoint: env.TRACING_ENDPOINT, serviceName: env.TRACING_SERVICE_NAME, }, websocket: { pingTimeout: env.WS_PING_TIMEOUT, pingInterval: env.WS_PING_INTERVAL, maxConnections: env.WS_MAX_CONNECTIONS, maxMessageSize: env.WS_MAX_MESSAGE_SIZE, }, mcp: { protocolVersion: env.MCP_PROTOCOL_VERSION, maxTools: env.MCP_MAX_TOOLS, maxResources: env.MCP_MAX_RESOURCES, timeout: env.MCP_TIMEOUT, rateLimitPerConnection: env.MCP_RATE_LIMIT_PER_CONNECTION, }, clustering: { enabled: env.CLUSTERING_ENABLED, workers: env.CLUSTERING_WORKERS === 'auto' ? 'auto' : parseInt(env.CLUSTERING_WORKERS, 10), }, healthCheck: { timeout: env.HEALTH_CHECK_TIMEOUT, interval: env.HEALTH_CHECK_INTERVAL, }, encryption: { algorithm: env.ENCRYPTION_ALGORITHM, keyDerivation: env.ENCRYPTION_KEY_DERIVATION, iterations: env.ENCRYPTION_ITERATIONS, }, session: { secret: env.SESSION_SECRET, timeout: env.SESSION_TIMEOUT, cookie: { secure: env.SESSION_COOKIE_SECURE, httpOnly: env.SESSION_COOKIE_HTTP_ONLY, sameSite: env.SESSION_COOKIE_SAME_SITE, }, }, }; // Configuration validation function export const validateConfig = (): void => { const requiredInProduction = [ 'DATABASE_URL', 'REDIS_URL', 'VAULT_URL', 'VAULT_TOKEN', 'JWT_SECRET', 'SESSION_SECRET', ]; if (config.env === 'production') { const missing = requiredInProduction.filter(key => !process.env[key]); if (missing.length > 0) { throw new Error(`Missing required production environment variables: ${missing.join(', ')}`); } // Additional production validations if (config.jwt.secret.length < 32) { throw new Error('JWT_SECRET must be at least 32 characters in production'); } if (config.session.secret.length < 32) { throw new Error('SESSION_SECRET must be at least 32 characters in production'); } if (!config.security.forceHttps) { console.warn('WARNING: SECURITY_FORCE_HTTPS is disabled in production'); } if (!config.session.cookie.secure) { console.warn('WARNING: SESSION_COOKIE_SECURE is disabled in production'); } } console.log('Configuration validated successfully:', { environment: config.env, server: `${config.server.host}:${config.server.port}`, clustering: config.clustering.enabled, monitoring: config.monitoring.enabled, tracing: config.tracing.enabled, }); }; // Export environment schema for testing export { envSchema };

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/perfecxion-ai/secure-mcp'

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