UNPKG

bmad-method-mcp

Version:

Breakthrough Method of Agile AI-driven Development with Enhanced MCP Integration

275 lines (244 loc) 6.96 kB
const fs = require('fs'); const path = require('path'); const yaml = require('js-yaml'); /** * Configuration Loader for BMad MCP Server * Handles loading and merging configuration from multiple sources */ class ConfigLoader { constructor() { this.config = null; this.configPaths = [ path.join(__dirname, 'bmad-mcp-config.yaml'), path.join(process.cwd(), 'bmad-mcp-config.yaml'), path.join(process.cwd(), '.bmad', 'mcp-config.yaml') ]; } /** * Load configuration from files and environment variables * @returns {Object} Merged configuration */ load() { if (this.config) { return this.config; } // Start with default config this.config = this.getDefaultConfig(); // Load from config files for (const configPath of this.configPaths) { if (fs.existsSync(configPath)) { try { const fileConfig = yaml.load(fs.readFileSync(configPath, 'utf8')); this.config = this.mergeConfig(this.config, fileConfig); console.log(`Loaded config from: ${configPath}`); } catch (error) { console.warn(`Failed to load config from ${configPath}:`, error.message); } } } // Override with environment variables this.applyEnvironmentOverrides(); return this.config; } /** * Get default configuration * @returns {Object} Default config */ getDefaultConfig() { return { webui: { auto_start: true, auto_open: process.env.NODE_ENV !== 'production', port: 5173, enabled_modes: ['mcp', 'dual'] }, server: { rest_port: 3001, log_level: 'info', request_logging: false }, database: { auto_create: true, filename: 'project.db', wal_mode: true }, mcp: { name: 'bmad-unified-server', version: '2.0.0', description: 'Unified MCP/REST server for BMAD Method with service architecture' }, development: { hot_reload: false, verbose_logging: false, auto_restart: false } }; } /** * Apply environment variable overrides */ applyEnvironmentOverrides() { // Web UI settings if (process.env.BMAD_AUTO_START_WEBUI !== undefined) { this.config.webui.auto_start = process.env.BMAD_AUTO_START_WEBUI === 'true'; } if (process.env.BMAD_AUTO_OPEN_WEBUI !== undefined) { this.config.webui.auto_open = process.env.BMAD_AUTO_OPEN_WEBUI === 'true'; } if (process.env.WEBUI_PORT) { this.config.webui.port = parseInt(process.env.WEBUI_PORT); } // Server settings if (process.env.PORT) { this.config.server.rest_port = parseInt(process.env.PORT); } if (process.env.LOG_LEVEL) { this.config.server.log_level = process.env.LOG_LEVEL.toLowerCase(); } // Database settings if (process.env.DB_FILE) { this.config.database.filename = process.env.DB_FILE; } // Development settings if (process.env.NODE_ENV === 'development') { this.config.development.verbose_logging = true; this.config.server.request_logging = true; } } /** * Deep merge configuration objects * @param {Object} target - Target config * @param {Object} source - Source config to merge * @returns {Object} Merged config */ mergeConfig(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = this.mergeConfig(result[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } /** * Get specific configuration section * @param {string} section - Config section name * @returns {Object} Configuration section */ get(section) { const config = this.load(); return section ? config[section] : config; } /** * Check if Web UI auto-start is enabled for current server mode * @param {string} serverMode - Current server mode * @returns {boolean} Whether to auto-start Web UI */ shouldAutoStartWebUI(serverMode = 'dual') { const config = this.load(); return config.webui.auto_start && config.webui.enabled_modes.includes(serverMode); } /** * Check if Web UI should auto-open in browser * @returns {boolean} Whether to auto-open Web UI */ shouldAutoOpenWebUI() { const config = this.load(); return config.webui.auto_open; } /** * Get Web UI port * @returns {number} Web UI port */ getWebUIPort() { const config = this.load(); return config.webui.port; } /** * Get REST API port * @returns {number} REST API port */ getRESTPort() { const config = this.load(); return config.server.rest_port; } /** * Get log level * @returns {string} Log level */ getLogLevel() { const config = this.load(); return config.server.log_level; } /** * Generate Claude Desktop MCP configuration * @param {string} projectPath - Project directory path * @returns {Object} Claude Desktop MCP config */ generateClaudeDesktopConfig(projectPath = process.cwd()) { const config = this.load(); return { command: "npx", args: ["bmad-method-mcp", "unified-server", "--mode=mcp"], cwd: projectPath, env: { BMAD_AUTO_START_WEBUI: config.webui.auto_start.toString(), BMAD_AUTO_OPEN_WEBUI: config.webui.auto_open.toString(), LOG_LEVEL: config.server.log_level, WEBUI_PORT: config.webui.port.toString() } }; } /** * Save current configuration to file * @param {string} filePath - File path to save config */ save(filePath = path.join(process.cwd(), 'bmad-mcp-config.yaml')) { const config = this.load(); const yamlStr = yaml.dump(config, { indent: 2, lineWidth: 80, noRefs: true }); fs.writeFileSync(filePath, yamlStr, 'utf8'); console.log(`Configuration saved to: ${filePath}`); } /** * Reload configuration from files */ reload() { this.config = null; return this.load(); } /** * Get installation configuration for use by installers * @returns {Object} Installation configuration */ getInstallationConfig() { const config = this.load(); return { webui: { auto_start: config.webui.auto_start, auto_open: config.webui.auto_open, port: config.webui.port }, server: { rest_port: config.server.rest_port, log_level: config.server.log_level }, database: { filename: config.database.filename, auto_create: config.database.auto_create }, mcp: { name: config.mcp.name, version: config.mcp.version } }; } } // Export singleton instance module.exports = new ConfigLoader();