@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
653 lines • 28.3 kB
JavaScript
/**
* Configuration management system for Optimizely MCP Server
* @description Handles loading and validation of configuration from environment variables,
* config files, and default values with proper error handling and validation
*/
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { getLogger } from '../logging/Logger.js';
import { MCPErrorMapper } from '../errors/MCPErrorMapping.js';
// Get project root directory (two levels up from src/config/)
const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
/**
* Configuration validation error with detailed context
*/
export class ConfigValidationError extends Error {
/** Field that failed validation */
field;
/** Expected value format or type */
expected;
/** Actual value that was provided */
actual;
/**
* Creates a new configuration validation error
* @param field - The configuration field that failed validation
* @param expected - Description of what was expected
* @param actual - The actual value that was provided
*/
constructor(field, expected, actual) {
super(`Configuration validation failed for '${field}': expected ${expected}, got ${typeof actual === 'string' ? `"${actual}"` : actual}`);
this.name = 'ConfigValidationError';
this.field = field;
this.expected = expected;
this.actual = actual;
}
}
/**
* Configuration manager for loading and validating MCP server settings
* @description Provides centralized configuration management with support for
* environment variables, JSON config files, and runtime validation
*/
export class ConfigManager {
config;
configFilePath;
/**
* Creates a new ConfigManager instance
* @param configFilePath - Optional path to JSON configuration file
*/
constructor(configFilePath) {
this.configFilePath = configFilePath;
this.config = this.getDefaultConfig();
}
/**
* Gets the default configuration with sensible defaults
* @returns Default configuration object
* @private
*/
getDefaultConfig() {
// Debug logging for path issues
if (!PROJECT_ROOT) {
getLogger().error('CRITICAL: PROJECT_ROOT is undefined!');
getLogger().error('__dirname equivalent:', path.dirname(fileURLToPath(import.meta.url)));
getLogger().error('Calculated PROJECT_ROOT:', path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..'));
}
return {
optimizely: {
apiToken: '', // Must be provided
projects: {
allowedIds: [], // Must be configured
allowedNames: [],
maxProjects: 0, // 0 means unlimited
autoDiscoverAll: false
},
baseUrl: 'https://api.optimizely.com/v2',
flagsUrl: 'https://api.optimizely.com/flags/v1',
rateLimits: {
requestsPerMinute: 60,
requestsPerSecond: 10
},
retries: {
maxAttempts: 3,
baseDelay: 1000
}
},
storage: {
databasePath: './data/optimizely-cache.db',
backupDir: './data/backups',
verbose: false
},
cache: {
syncIntervalMinutes: 60,
autoSync: false,
maxCacheAgeHours: 24,
changeHistory: {
days: 1,
maxRecords: 10000,
disable: false
}
},
logging: {
level: 'info',
consoleLogging: false, // Safe default for MCP StdioServerTransport
logFile: './logs/optimizely-mcp.log',
prettyPrint: false,
maxFileSize: 10 * 1024 * 1024, // 10MB
maxFiles: 5
},
server: {
name: 'optimizely-mcp-server',
version: '1.0.0',
maxConcurrency: 10
},
mcp: {
transport: 'stdio',
requestTimeoutMs: 30000, // 30 seconds
debugMode: false,
tools: {
maxExecutionTimeMs: 120000, // 2 minutes
logInputOutput: false,
validateResponses: true
},
resources: {
maxContentSize: 10 * 1024 * 1024, // 10MB
enableCaching: true,
cacheTtlSeconds: 300 // 5 minutes
},
errors: {
includeStackTraces: false,
includeErrorContext: true,
logAllErrors: true
}
}
};
}
/**
* Loads configuration from all available sources in priority order
* @returns Promise that resolves when configuration is loaded and validated
* @throws {ConfigValidationError} When required configuration is missing or invalid
* @description Sources (in priority order): Environment variables, config file, defaults
*/
async loadConfig() {
try {
// Start with defaults
this.config = this.getDefaultConfig();
// Load from config file if specified
if (this.configFilePath) {
await this.loadFromFile();
}
// Override with environment variables (highest priority)
this.loadFromEnvironment();
// Validate the final configuration
this.validateConfig();
getLogger().info('Configuration loaded successfully');
}
catch (error) {
if (error instanceof ConfigValidationError) {
throw error;
}
throw MCPErrorMapper.toMCPError(error, 'Failed to load configuration');
}
}
/**
* Loads configuration from a JSON file
* @private
* @throws {Error} When file cannot be read or contains invalid JSON
*/
async loadFromFile() {
if (!this.configFilePath)
return;
try {
const fileContent = await fs.readFile(this.configFilePath, 'utf-8');
const fileConfig = JSON.parse(fileContent);
// Deep merge with existing config
this.config = this.deepMerge(this.config, fileConfig);
getLogger().info({ configFile: this.configFilePath }, 'Configuration loaded from file');
}
catch (error) {
if (error.code === 'ENOENT') {
getLogger().warn({ configFile: this.configFilePath }, 'Configuration file not found, using defaults');
}
else {
throw MCPErrorMapper.toMCPError(error, `Failed to load config file ${this.configFilePath}`);
}
}
}
/**
* Loads configuration from environment variables
* @private
* @description Supports nested configuration via underscore-separated variable names
*/
loadFromEnvironment() {
const env = process.env;
// Optimizely configuration
if (env.OPTIMIZELY_API_TOKEN) {
this.config.optimizely.apiToken = env.OPTIMIZELY_API_TOKEN;
}
// Project filtering configuration
if (env.OPTIMIZELY_PROJECT_IDS) {
this.config.optimizely.projects.allowedIds = env.OPTIMIZELY_PROJECT_IDS
.split(',')
.map(id => id.trim())
.filter(id => id.length > 0); // Remove empty strings from trailing commas
}
if (env.OPTIMIZELY_PROJECT_NAMES) {
this.config.optimizely.projects.allowedNames = env.OPTIMIZELY_PROJECT_NAMES
.split(',')
.map(name => name.trim())
.filter(name => name.length > 0); // Remove empty strings from trailing commas
}
if (env.OPTIMIZELY_MAX_PROJECTS) {
this.config.optimizely.projects.maxProjects = parseInt(env.OPTIMIZELY_MAX_PROJECTS, 10);
}
if (env.OPTIMIZELY_AUTO_DISCOVER_ALL) {
this.config.optimizely.projects.autoDiscoverAll = env.OPTIMIZELY_AUTO_DISCOVER_ALL.toLowerCase() === 'true';
}
if (env.OPTIMIZELY_BASE_URL) {
this.config.optimizely.baseUrl = env.OPTIMIZELY_BASE_URL;
}
if (env.OPTIMIZELY_FLAGS_URL) {
this.config.optimizely.flagsUrl = env.OPTIMIZELY_FLAGS_URL;
}
if (env.OPTIMIZELY_REQUESTS_PER_MINUTE) {
this.config.optimizely.rateLimits.requestsPerMinute = parseInt(env.OPTIMIZELY_REQUESTS_PER_MINUTE, 10);
}
if (env.OPTIMIZELY_REQUESTS_PER_SECOND) {
this.config.optimizely.rateLimits.requestsPerSecond = parseInt(env.OPTIMIZELY_REQUESTS_PER_SECOND, 10);
}
if (env.OPTIMIZELY_RETRY_MAX_ATTEMPTS) {
this.config.optimizely.retries.maxAttempts = parseInt(env.OPTIMIZELY_RETRY_MAX_ATTEMPTS, 10);
}
if (env.OPTIMIZELY_RETRY_BASE_DELAY) {
this.config.optimizely.retries.baseDelay = parseInt(env.OPTIMIZELY_RETRY_BASE_DELAY, 10);
}
// Storage configuration
if (env.STORAGE_DATABASE_PATH) {
this.config.storage.databasePath = env.STORAGE_DATABASE_PATH;
}
if (env.STORAGE_BACKUP_DIR) {
this.config.storage.backupDir = env.STORAGE_BACKUP_DIR;
}
if (env.STORAGE_VERBOSE) {
this.config.storage.verbose = env.STORAGE_VERBOSE.toLowerCase() === 'true';
}
// Cache configuration
if (env.CACHE_SYNC_INTERVAL_MINUTES) {
this.config.cache.syncIntervalMinutes = parseInt(env.CACHE_SYNC_INTERVAL_MINUTES, 10);
getLogger().debug({
CACHE_SYNC_INTERVAL_MINUTES: env.CACHE_SYNC_INTERVAL_MINUTES,
parsed: this.config.cache.syncIntervalMinutes
}, 'Loaded CACHE_SYNC_INTERVAL_MINUTES');
}
if (env.CACHE_AUTO_SYNC) {
this.config.cache.autoSync = env.CACHE_AUTO_SYNC.toLowerCase() === 'true';
getLogger().debug({
CACHE_AUTO_SYNC: env.CACHE_AUTO_SYNC,
parsed: this.config.cache.autoSync
}, 'Loaded CACHE_AUTO_SYNC');
}
if (env.CACHE_MAX_AGE_HOURS) {
this.config.cache.maxCacheAgeHours = parseInt(env.CACHE_MAX_AGE_HOURS, 10);
}
// Change history configuration
if (env.CHANGE_HISTORY_DAYS) {
this.config.cache.changeHistory.days = parseInt(env.CHANGE_HISTORY_DAYS, 10);
getLogger().debug({
CHANGE_HISTORY_DAYS: env.CHANGE_HISTORY_DAYS,
parsed: this.config.cache.changeHistory.days
}, 'Loaded CHANGE_HISTORY_DAYS');
}
if (env.CHANGE_HISTORY_MAX_RECORDS) {
this.config.cache.changeHistory.maxRecords = parseInt(env.CHANGE_HISTORY_MAX_RECORDS, 10);
getLogger().debug({
CHANGE_HISTORY_MAX_RECORDS: env.CHANGE_HISTORY_MAX_RECORDS,
parsed: this.config.cache.changeHistory.maxRecords
}, 'Loaded CHANGE_HISTORY_MAX_RECORDS');
}
if (env.CHANGE_HISTORY_DISABLE) {
this.config.cache.changeHistory.disable = env.CHANGE_HISTORY_DISABLE.toLowerCase() === 'true';
getLogger().debug({
CHANGE_HISTORY_DISABLE: env.CHANGE_HISTORY_DISABLE,
parsed: this.config.cache.changeHistory.disable
}, 'Loaded CHANGE_HISTORY_DISABLE');
}
// MCP-safe logging configuration
if (env.OPTIMIZELY_MCP_LOG_LEVEL) {
const level = env.OPTIMIZELY_MCP_LOG_LEVEL.toLowerCase();
if (['fatal', 'error', 'warn', 'info', 'debug', 'trace'].includes(level)) {
this.config.logging.level = level;
}
}
if (env.OPTIMIZELY_MCP_CONSOLE_LOGGING) {
this.config.logging.consoleLogging = env.OPTIMIZELY_MCP_CONSOLE_LOGGING.toLowerCase() === 'true';
}
if (env.OPTIMIZELY_MCP_LOG_FILE) {
this.config.logging.logFile = env.OPTIMIZELY_MCP_LOG_FILE;
}
if (env.OPTIMIZELY_MCP_PRETTY_PRINT) {
this.config.logging.prettyPrint = env.OPTIMIZELY_MCP_PRETTY_PRINT.toLowerCase() === 'true';
}
if (env.OPTIMIZELY_MCP_MAX_FILE_SIZE) {
this.config.logging.maxFileSize = parseInt(env.OPTIMIZELY_MCP_MAX_FILE_SIZE, 10);
}
if (env.OPTIMIZELY_MCP_MAX_FILES) {
this.config.logging.maxFiles = parseInt(env.OPTIMIZELY_MCP_MAX_FILES, 10);
}
// Server configuration
if (env.SERVER_NAME) {
this.config.server.name = env.SERVER_NAME;
}
if (env.SERVER_VERSION) {
this.config.server.version = env.SERVER_VERSION;
}
if (env.SERVER_MAX_CONCURRENCY) {
this.config.server.maxConcurrency = parseInt(env.SERVER_MAX_CONCURRENCY, 10);
}
// MCP protocol configuration
if (env.MCP_TRANSPORT) {
const transport = env.MCP_TRANSPORT.toLowerCase();
if (['stdio', 'sse', 'websocket'].includes(transport)) {
this.config.mcp.transport = transport;
}
}
if (env.MCP_REQUEST_TIMEOUT_MS) {
this.config.mcp.requestTimeoutMs = parseInt(env.MCP_REQUEST_TIMEOUT_MS, 10);
}
if (env.MCP_DEBUG_MODE) {
this.config.mcp.debugMode = env.MCP_DEBUG_MODE.toLowerCase() === 'true';
}
// MCP tools configuration
if (env.MCP_TOOLS_MAX_EXECUTION_TIME_MS) {
this.config.mcp.tools.maxExecutionTimeMs = parseInt(env.MCP_TOOLS_MAX_EXECUTION_TIME_MS, 10);
}
if (env.MCP_TOOLS_LOG_INPUT_OUTPUT) {
this.config.mcp.tools.logInputOutput = env.MCP_TOOLS_LOG_INPUT_OUTPUT.toLowerCase() === 'true';
}
if (env.MCP_TOOLS_VALIDATE_RESPONSES) {
this.config.mcp.tools.validateResponses = env.MCP_TOOLS_VALIDATE_RESPONSES.toLowerCase() === 'true';
}
// MCP resources configuration
if (env.MCP_RESOURCES_MAX_CONTENT_SIZE) {
this.config.mcp.resources.maxContentSize = parseInt(env.MCP_RESOURCES_MAX_CONTENT_SIZE, 10);
}
if (env.MCP_RESOURCES_ENABLE_CACHING) {
this.config.mcp.resources.enableCaching = env.MCP_RESOURCES_ENABLE_CACHING.toLowerCase() === 'true';
}
if (env.MCP_RESOURCES_CACHE_TTL_SECONDS) {
this.config.mcp.resources.cacheTtlSeconds = parseInt(env.MCP_RESOURCES_CACHE_TTL_SECONDS, 10);
}
// MCP errors configuration
if (env.MCP_ERRORS_INCLUDE_STACK_TRACES) {
this.config.mcp.errors.includeStackTraces = env.MCP_ERRORS_INCLUDE_STACK_TRACES.toLowerCase() === 'true';
}
if (env.MCP_ERRORS_INCLUDE_ERROR_CONTEXT) {
this.config.mcp.errors.includeErrorContext = env.MCP_ERRORS_INCLUDE_ERROR_CONTEXT.toLowerCase() === 'true';
}
if (env.MCP_ERRORS_LOG_ALL_ERRORS) {
this.config.mcp.errors.logAllErrors = env.MCP_ERRORS_LOG_ALL_ERRORS.toLowerCase() === 'true';
}
getLogger().debug('Environment variables processed');
}
/**
* Validates the loaded configuration for required fields and value constraints
* @private
* @throws {ConfigValidationError} When validation fails
*/
validateConfig() {
// Required fields
if (!this.config.optimizely.apiToken) {
throw new ConfigValidationError('optimizely.apiToken', 'non-empty string (set OPTIMIZELY_API_TOKEN environment variable)', this.config.optimizely.apiToken);
}
// Project filtering validation - CRITICAL for preventing accidental full sync
const projects = this.config.optimizely.projects;
const hasProjectIds = projects.allowedIds && projects.allowedIds.length > 0;
const hasProjectNames = projects.allowedNames && projects.allowedNames.length > 0;
const autoDiscover = projects.autoDiscoverAll;
if (!hasProjectIds && !hasProjectNames && !autoDiscover) {
throw new ConfigValidationError('optimizely.projects', 'at least one of: allowedIds, allowedNames, or autoDiscoverAll=true (set OPTIMIZELY_PROJECT_IDS, OPTIMIZELY_PROJECT_NAMES, or OPTIMIZELY_AUTO_DISCOVER_ALL)', 'no project filtering configured');
}
// Safety check for auto-discover
if (autoDiscover && !hasProjectIds && !hasProjectNames) {
getLogger().warn('Auto-discover mode enabled without project filtering - will sync ALL accessible projects');
}
// Validate max projects limit (0 means unlimited)
if (projects.maxProjects !== undefined && (projects.maxProjects < 0 || projects.maxProjects > 50)) {
throw new ConfigValidationError('optimizely.projects.maxProjects', 'number between 0 and 50 (0 for unlimited)', projects.maxProjects);
}
// URL validation
if (this.config.optimizely.baseUrl && !this.isValidUrl(this.config.optimizely.baseUrl)) {
throw new ConfigValidationError('optimizely.baseUrl', 'valid URL', this.config.optimizely.baseUrl);
}
if (this.config.optimizely.flagsUrl && !this.isValidUrl(this.config.optimizely.flagsUrl)) {
throw new ConfigValidationError('optimizely.flagsUrl', 'valid URL', this.config.optimizely.flagsUrl);
}
// Numeric validations
if (this.config.optimizely.rateLimits?.requestsPerMinute !== undefined) {
if (!Number.isInteger(this.config.optimizely.rateLimits.requestsPerMinute) ||
this.config.optimizely.rateLimits.requestsPerMinute <= 0) {
throw new ConfigValidationError('optimizely.rateLimits.requestsPerMinute', 'positive integer', this.config.optimizely.rateLimits.requestsPerMinute);
}
}
if (this.config.optimizely.rateLimits?.requestsPerSecond !== undefined) {
if (!Number.isInteger(this.config.optimizely.rateLimits.requestsPerSecond) ||
this.config.optimizely.rateLimits.requestsPerSecond <= 0) {
throw new ConfigValidationError('optimizely.rateLimits.requestsPerSecond', 'positive integer', this.config.optimizely.rateLimits.requestsPerSecond);
}
}
// Path validations (basic check for string)
if (this.config.storage.databasePath && typeof this.config.storage.databasePath !== 'string') {
throw new ConfigValidationError('storage.databasePath', 'string path', this.config.storage.databasePath);
}
// MCP configuration validations
if (this.config.mcp.requestTimeoutMs !== undefined) {
if (!Number.isInteger(this.config.mcp.requestTimeoutMs) ||
this.config.mcp.requestTimeoutMs <= 0 ||
this.config.mcp.requestTimeoutMs > 300000) { // 5 minutes max
throw new ConfigValidationError('mcp.requestTimeoutMs', 'positive integer between 1 and 300000 (5 minutes)', this.config.mcp.requestTimeoutMs);
}
}
if (this.config.mcp.tools?.maxExecutionTimeMs !== undefined) {
if (!Number.isInteger(this.config.mcp.tools.maxExecutionTimeMs) ||
this.config.mcp.tools.maxExecutionTimeMs <= 0 ||
this.config.mcp.tools.maxExecutionTimeMs > 600000) { // 10 minutes max
throw new ConfigValidationError('mcp.tools.maxExecutionTimeMs', 'positive integer between 1 and 600000 (10 minutes)', this.config.mcp.tools.maxExecutionTimeMs);
}
}
if (this.config.mcp.resources?.maxContentSize !== undefined) {
if (!Number.isInteger(this.config.mcp.resources.maxContentSize) ||
this.config.mcp.resources.maxContentSize <= 0 ||
this.config.mcp.resources.maxContentSize > 100 * 1024 * 1024) { // 100MB max
throw new ConfigValidationError('mcp.resources.maxContentSize', 'positive integer between 1 and 104857600 (100MB)', this.config.mcp.resources.maxContentSize);
}
}
if (this.config.mcp.resources?.cacheTtlSeconds !== undefined) {
if (!Number.isInteger(this.config.mcp.resources.cacheTtlSeconds) ||
this.config.mcp.resources.cacheTtlSeconds <= 0) {
throw new ConfigValidationError('mcp.resources.cacheTtlSeconds', 'positive integer', this.config.mcp.resources.cacheTtlSeconds);
}
}
getLogger().debug('Configuration validation completed successfully');
}
/**
* Validates if a string is a proper URL
* @param urlString - The string to validate
* @returns True if the string is a valid URL
* @private
*/
isValidUrl(urlString) {
try {
new URL(urlString);
return true;
}
catch {
return false;
}
}
/**
* Deep merges two configuration objects
* @param target - The target object to merge into
* @param source - The source object to merge from
* @returns The merged configuration object
* @private
*/
deepMerge(target, source) {
const result = { ...target };
for (const key in source) {
if (source[key] !== null && typeof source[key] === 'object' && !Array.isArray(source[key])) {
result[key] = this.deepMerge(target[key] || {}, source[key]);
}
else {
result[key] = source[key];
}
}
return result;
}
/**
* Gets the current configuration
* @returns The loaded and validated configuration
*/
getConfig() {
return this.config;
}
/**
* Gets the Optimizely API configuration specifically
* @returns Optimizely-specific configuration
*/
getOptimizelyConfig() {
return this.config.optimizely;
}
/**
* Gets the storage configuration specifically
* @returns Storage-specific configuration
*/
getStorageConfig() {
return this.config.storage;
}
/**
* Gets the cache configuration specifically
* @returns Cache-specific configuration
*/
getCacheConfig() {
return this.config.cache;
}
/**
* Gets the logging configuration specifically
* @returns Logging-specific configuration
*/
getLoggingConfig() {
return this.config.logging;
}
/**
* Gets the server configuration specifically
* @returns Server-specific configuration
*/
getServerConfig() {
return this.config.server;
}
/**
* Gets the MCP protocol configuration specifically
* @returns MCP-specific configuration
*/
getMcpConfig() {
return this.config.mcp;
}
/**
* Saves the current configuration to a JSON file
* @param filePath - Path where to save the configuration
* @param includeSecrets - Whether to include sensitive data like API tokens
* @returns Promise that resolves when file is written
*/
async saveConfigToFile(filePath, includeSecrets = false) {
try {
const configToSave = { ...this.config };
if (!includeSecrets) {
// Remove sensitive information
configToSave.optimizely.apiToken = '[REDACTED]';
}
// Ensure directory exists
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, JSON.stringify(configToSave, null, 2), 'utf-8');
getLogger().info({ filePath }, 'Configuration saved');
}
catch (error) {
throw MCPErrorMapper.toMCPError(error, 'Failed to save configuration');
}
}
/**
* Creates a sample configuration file with documentation
* @param filePath - Path where to create the sample file
* @returns Promise that resolves when sample file is created
*/
async createSampleConfig(filePath) {
const sampleConfig = {
"_comment": "Optimizely MCP Server Configuration File",
"_note": "Environment variables take precedence over file settings",
"optimizely": {
"_comment": "Optimizely API settings",
"apiToken": "YOUR_OPTIMIZELY_API_TOKEN_HERE",
"baseUrl": "https://api.optimizely.com/v2",
"flagsUrl": "https://api.optimizely.com/flags/v1",
"rateLimits": {
"requestsPerMinute": 60,
"requestsPerSecond": 10
},
"retries": {
"maxAttempts": 3,
"baseDelay": 1000
}
},
"storage": {
"_comment": "Database and storage settings",
"databasePath": "./data/optimizely-cache.db",
"backupDir": "./data/backups",
"verbose": false
},
"cache": {
"_comment": "Cache and synchronization settings",
"syncIntervalMinutes": 60,
"autoSync": false,
"maxCacheAgeHours": 24
},
"logging": {
"_comment": "Logging configuration",
"level": "info",
"console": true,
"file": "./logs/mcp-server.log"
},
"server": {
"_comment": "MCP server settings",
"name": "optimizely-mcp-server",
"version": "1.0.0",
"maxConcurrency": 10
},
"mcp": {
"_comment": "MCP protocol specific configuration",
"transport": "stdio",
"requestTimeoutMs": 30000,
"debugMode": false,
"tools": {
"_comment": "Tool execution settings",
"maxExecutionTimeMs": 120000,
"logInputOutput": false,
"validateResponses": true
},
"resources": {
"_comment": "Resource access settings",
"maxContentSize": 10485760,
"enableCaching": true,
"cacheTtlSeconds": 300
},
"errors": {
"_comment": "Error handling settings",
"includeStackTraces": false,
"includeErrorContext": true,
"logAllErrors": true
}
}
};
try {
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, JSON.stringify(sampleConfig, null, 2), 'utf-8');
getLogger().info({ filePath }, 'Sample configuration created');
}
catch (error) {
throw MCPErrorMapper.toMCPError(error, 'Failed to create sample configuration');
}
}
}
// Create singleton instance for global use
let configManagerInstance = null;
/**
* Gets the global configuration manager instance
* @param configFilePath - Optional path to configuration file (used only on first call)
* @returns The global ConfigManager instance
* @description Implements singleton pattern for consistent configuration access
*/
export function getConfigManager(configFilePath) {
if (!configManagerInstance) {
configManagerInstance = new ConfigManager(configFilePath);
}
return configManagerInstance;
}
/**
* Initializes the global configuration manager with environment-based config file detection
* @returns Promise that resolves when configuration is loaded
* @description Automatically detects config file from MCP_CONFIG_FILE environment variable
*/
export async function initializeConfig() {
const configFilePath = process.env.MCP_CONFIG_FILE;
const manager = getConfigManager(configFilePath);
await manager.loadConfig();
return manager;
}
//# sourceMappingURL=ConfigManager.js.map