UNPKG

bc-webclient-mcp

Version:

Model Context Protocol (MCP) server for Microsoft Dynamics 365 Business Central via WebUI protocol. Enables AI assistants to interact with BC through the web client protocol, supporting Card, List, and Document pages with full line item support and server

206 lines 6.61 kB
/** * Centralized Configuration Module * * Type-safe environment variable management for the BC MCP Server. * All environment variables should be accessed through this module. */ import * as dotenv from 'dotenv'; // Load environment variables from .env file dotenv.config(); /** * Parse and validate environment variables */ class Config { config; constructor() { this.config = this.parseEnvironment(); this.validateConfig(); } /** * Parse environment variables with defaults */ parseEnvironment() { return { nodeEnv: this.getNodeEnv(), logLevel: this.getLogLevel(), bc: { baseUrl: process.env.BC_BASE_URL || 'http://Cronus27/BC', username: process.env.BC_USERNAME || 'sshadows', password: process.env.BC_PASSWORD || '1234', tenantId: process.env.BC_TENANT_ID || 'default', timeout: this.getNumberEnv('BC_TIMEOUT', 120000), searchTimingWindowMs: this.getNumberEnv('BC_SEARCH_TIMING_WINDOW_MS', 15000), }, debug: this.parseDebugConfig(), }; } /** * Parse debug logging configuration */ parseDebugConfig() { const enabled = this.getBooleanEnv('DEBUG_MODE', false); const channelsStr = process.env.DEBUG_CHANNELS || 'all'; const channelsList = channelsStr.split(',').map((c) => c.trim()); // If 'all' is specified, enable all channels (except 'all' itself) const channels = new Set(channelsList); if (channels.has('all')) { return { enabled, logDir: process.env.DEBUG_LOG_DIR || '.debug-logs', maxSizeMB: this.getNumberEnv('DEBUG_LOG_MAX_SIZE_MB', 50), maxFiles: this.getNumberEnv('DEBUG_LOG_MAX_FILES', 10), channels: new Set(['stdio', 'tools', 'websocket', 'handlers', 'session', 'cache']), logFullHandlers: this.getBooleanEnv('DEBUG_LOG_FULL_HANDLERS', false), logFullWsMessages: this.getBooleanEnv('DEBUG_LOG_FULL_WS_MESSAGES', true), }; } return { enabled, logDir: process.env.DEBUG_LOG_DIR || '.debug-logs', maxSizeMB: this.getNumberEnv('DEBUG_LOG_MAX_SIZE_MB', 50), maxFiles: this.getNumberEnv('DEBUG_LOG_MAX_FILES', 10), channels, logFullHandlers: this.getBooleanEnv('DEBUG_LOG_FULL_HANDLERS', false), logFullWsMessages: this.getBooleanEnv('DEBUG_LOG_FULL_WS_MESSAGES', true), }; } /** * Get Node environment with validation */ getNodeEnv() { const env = process.env.NODE_ENV?.toLowerCase(); if (env === 'production' || env === 'test') { return env; } return 'development'; } /** * Get log level with validation */ getLogLevel() { const level = process.env.LOG_LEVEL?.toLowerCase(); if (level === 'debug' || level === 'info' || level === 'warn' || level === 'error') { return level; } return 'info'; } /** * Get numeric environment variable with default */ getNumberEnv(key, defaultValue) { const value = process.env[key]; if (!value) { return defaultValue; } const parsed = parseInt(value, 10); if (isNaN(parsed)) { console.error(`Invalid number for ${key}="${value}", using default: ${defaultValue}`); return defaultValue; } return parsed; } /** * Get boolean environment variable with default */ getBooleanEnv(key, defaultValue) { const value = process.env[key]; if (!value) { return defaultValue; } return value.toLowerCase() === 'true' || value === '1'; } /** * Validate configuration */ validateConfig() { // Validate BC configuration if (!this.config.bc.baseUrl) { throw new Error('BC_BASE_URL is required'); } if (!this.config.bc.username) { throw new Error('BC_USERNAME is required'); } // Password can be empty for some environments if (this.config.bc.password === '') { console.error('BC_PASSWORD is empty - authentication may fail'); } // Validate URL format try { new URL(this.config.bc.baseUrl); } catch (error) { throw new Error(`Invalid BC_BASE_URL: ${this.config.bc.baseUrl}`); } // Log configuration (without sensitive data) - use console.error to write to stderr for MCP stdio compatibility if (process.env.NODE_ENV !== 'test') { console.error('Configuration loaded:', { nodeEnv: this.config.nodeEnv, logLevel: this.config.logLevel, bcBaseUrl: this.config.bc.baseUrl, bcUsername: this.config.bc.username, bcTenantId: this.config.bc.tenantId, bcTimeout: this.config.bc.timeout, }); } } /** * Get the full configuration */ getConfig() { return this.config; } /** * Get Node environment */ get nodeEnv() { return this.config.nodeEnv; } /** * Get log level */ get logLevel() { return this.config.logLevel; } /** * Get BC configuration */ get bc() { return this.config.bc; } /** * Get debug configuration */ get debug() { return this.config.debug; } /** * Check if running in development mode */ get isDevelopment() { return this.config.nodeEnv === 'development'; } /** * Check if running in production mode */ get isProduction() { return this.config.nodeEnv === 'production'; } /** * Check if running in test mode */ get isTest() { return this.config.nodeEnv === 'test'; } } // Create singleton instance const configInstance = new Config(); // Export the singleton export const config = configInstance; // Export convenience accessors export const bcConfig = configInstance.bc; export const isDevelopment = configInstance.isDevelopment; export const isProduction = configInstance.isProduction; export const isTest = configInstance.isTest; export const nodeEnv = configInstance.nodeEnv; export const logLevel = configInstance.logLevel; //# sourceMappingURL=config.js.map