UNPKG

mcp-adr-analysis-server

Version:

MCP server for analyzing Architectural Decision Records and project architecture

489 lines 20.5 kB
/** * Environment Capability Registry * * Auto-detects and queries available runtime environment capabilities: * - Kubernetes/OpenShift clusters * - Docker/Podman containers * - Operating system resources * - Red Hat tooling (Ansible, etc.) */ import { exec } from 'node:child_process'; import { promisify } from 'util'; import * as os from 'os'; import { EnhancedLogger } from './enhanced-logging.js'; export class EnvironmentCapabilityRegistry { capabilities = new Map(); logger; projectPath; discoveryComplete = false; execAsync; constructor(projectPath, execFunction) { this.logger = new EnhancedLogger(); this.projectPath = projectPath || process.cwd(); this.execAsync = execFunction || promisify(exec); } /** * Discover all available environment capabilities */ async discoverCapabilities() { if (this.discoveryComplete) { return; } this.logger.info('Starting environment capability discovery', 'EnvironmentCapabilityRegistry'); // Register all known capabilities await this.registerOSCapability(); await this.registerDockerCapability(); await this.registerPodmanCapability(); await this.registerKubernetesCapability(); await this.registerOpenShiftCapability(); await this.registerAnsibleCapability(); this.discoveryComplete = true; const availableCapabilities = this.listCapabilities(); this.logger.info(`Discovery complete: ${availableCapabilities.length} capabilities available`, 'EnvironmentCapabilityRegistry', { capabilities: availableCapabilities }); } /** * Register Operating System capability (always available) */ async registerOSCapability() { const capability = { name: 'operating-system', type: 'os', detector: async () => true, // Always available executor: async (query) => { const queryLower = query.toLowerCase(); const result = { platform: process.platform, arch: os.arch(), release: os.release(), type: os.type(), }; if (queryLower.includes('cpu') || queryLower.includes('processor')) { result.cpus = os.cpus(); result.cpuCount = os.cpus().length; } if (queryLower.includes('memory') || queryLower.includes('ram')) { result.totalMemory = os.totalmem(); result.freeMemory = os.freemem(); result.memoryUsagePercent = ((os.totalmem() - os.freemem()) / os.totalmem()) * 100; } if (queryLower.includes('disk') || queryLower.includes('storage')) { try { const { stdout } = await this.execAsync('df -h'); result.diskUsage = stdout; } catch { result.diskUsage = 'Unable to query disk usage'; } } if (queryLower.includes('network')) { result.networkInterfaces = os.networkInterfaces(); } return result; }, metadata: { version: os.release(), provider: os.type(), available: true, }, }; await this.registerCapability(capability); } /** * Register Docker capability */ async registerDockerCapability() { const capability = { name: 'docker', type: 'docker', detector: async () => { try { await this.execAsync('docker version --format "{{.Server.Version}}"'); return true; } catch { return false; } }, executor: async (query) => { const queryLower = query.toLowerCase(); const result = {}; try { if (queryLower.includes('container') || queryLower.includes('running') || queryLower.includes('status')) { const { stdout: ps } = await this.execAsync('docker ps --format json'); result.runningContainers = ps .trim() .split('\n') .filter(line => line) .map(line => JSON.parse(line)); } if (queryLower.includes('image')) { const { stdout: images } = await this.execAsync('docker images --format json'); result.images = images .trim() .split('\n') .filter(line => line) .map(line => JSON.parse(line)); } if (queryLower.includes('network')) { const { stdout: networks } = await this.execAsync('docker network ls --format json'); result.networks = networks .trim() .split('\n') .filter(line => line) .map(line => JSON.parse(line)); } if (queryLower.includes('volume')) { const { stdout: volumes } = await this.execAsync('docker volume ls --format json'); result.volumes = volumes .trim() .split('\n') .filter(line => line) .map(line => JSON.parse(line)); } return result; } catch (_error) { return { error: 'Failed to query Docker', details: _error.message }; } }, }; await this.registerCapability(capability); } /** * Register Podman capability (Red Hat container runtime) */ async registerPodmanCapability() { const capability = { name: 'podman', type: 'podman', detector: async () => { try { await this.execAsync('podman version --format json'); return true; } catch { return false; } }, executor: async (query) => { const queryLower = query.toLowerCase(); const result = {}; try { if (queryLower.includes('container') || queryLower.includes('pod') || queryLower.includes('running')) { const { stdout: ps } = await this.execAsync('podman ps --format json'); result.runningContainers = JSON.parse(ps); // Check for pods (unique to Podman) const { stdout: pods } = await this.execAsync('podman pod ps --format json'); result.pods = JSON.parse(pods); } if (queryLower.includes('image')) { const { stdout: images } = await this.execAsync('podman images --format json'); result.images = JSON.parse(images); } if (queryLower.includes('network')) { const { stdout: networks } = await this.execAsync('podman network ls --format json'); result.networks = JSON.parse(networks); } return result; } catch (_error) { return { error: 'Failed to query Podman', details: _error.message }; } }, }; await this.registerCapability(capability); } /** * Register Kubernetes capability */ async registerKubernetesCapability() { const capability = { name: 'kubernetes', type: 'kubernetes', detector: async () => { try { await this.execAsync('kubectl version --client --output=json'); return true; } catch { return false; } }, executor: async (query) => { const queryLower = query.toLowerCase(); const result = {}; try { // Get current context const { stdout: context } = await this.execAsync('kubectl config current-context'); result.currentContext = context.trim(); if (queryLower.includes('pod')) { const { stdout: pods } = await this.execAsync('kubectl get pods --all-namespaces -o json'); result.pods = JSON.parse(pods); } if (queryLower.includes('deployment')) { const { stdout: deployments } = await this.execAsync('kubectl get deployments --all-namespaces -o json'); result.deployments = JSON.parse(deployments); } if (queryLower.includes('service')) { const { stdout: services } = await this.execAsync('kubectl get services --all-namespaces -o json'); result.services = JSON.parse(services); } if (queryLower.includes('node')) { const { stdout: nodes } = await this.execAsync('kubectl get nodes -o json'); result.nodes = JSON.parse(nodes); } if (queryLower.includes('namespace')) { const { stdout: namespaces } = await this.execAsync('kubectl get namespaces -o json'); result.namespaces = JSON.parse(namespaces); } if (queryLower.includes('resource') || queryLower.includes('usage') || queryLower.includes('metrics')) { try { const { stdout: metrics } = await this.execAsync('kubectl top nodes'); result.nodeMetrics = metrics; } catch { result.nodeMetrics = 'Metrics server not available'; } } return result; } catch (_error) { return { error: 'Failed to query Kubernetes', details: _error.message }; } }, }; await this.registerCapability(capability); } /** * Register OpenShift capability (Red Hat Kubernetes distribution) */ async registerOpenShiftCapability() { const capability = { name: 'openshift', type: 'openshift', detector: async () => { try { await this.execAsync('oc version --client -o json'); return true; } catch { return false; } }, executor: async (query) => { const queryLower = query.toLowerCase(); const result = {}; try { // Get current project (OpenShift terminology for namespace) const { stdout: project } = await this.execAsync('oc project -q'); result.currentProject = project.trim(); // Get cluster info const { stdout: version } = await this.execAsync('oc version -o json'); result.version = JSON.parse(version); if (queryLower.includes('pod')) { const { stdout: pods } = await this.execAsync('oc get pods --all-namespaces -o json'); result.pods = JSON.parse(pods); } if (queryLower.includes('deployment') || queryLower.includes('dc')) { const { stdout: dc } = await this.execAsync('oc get dc --all-namespaces -o json'); result.deploymentConfigs = JSON.parse(dc); } if (queryLower.includes('route')) { const { stdout: routes } = await this.execAsync('oc get routes --all-namespaces -o json'); result.routes = JSON.parse(routes); } if (queryLower.includes('build') || queryLower.includes('buildconfig')) { const { stdout: builds } = await this.execAsync('oc get builds --all-namespaces -o json'); result.builds = JSON.parse(builds); } if (queryLower.includes('project') || queryLower.includes('namespace')) { const { stdout: projects } = await this.execAsync('oc get projects -o json'); result.projects = JSON.parse(projects); } return result; } catch (_error) { return { error: 'Failed to query OpenShift', details: _error.message }; } }, }; await this.registerCapability(capability); } /** * Register Ansible capability (Red Hat automation) */ async registerAnsibleCapability() { const capability = { name: 'ansible', type: 'ansible', detector: async () => { try { await this.execAsync('ansible --version'); return true; } catch { return false; } }, executor: async (query) => { const queryLower = query.toLowerCase(); const result = {}; try { // Get Ansible version const { stdout: version } = await this.execAsync('ansible --version'); result.version = version; if (queryLower.includes('inventory') || queryLower.includes('host')) { try { const { stdout: inventory } = await this.execAsync('ansible-inventory --list'); result.inventory = JSON.parse(inventory); } catch { result.inventory = 'No inventory file found'; } } if (queryLower.includes('playbook')) { try { const { stdout: playbooks } = await this.execAsync(`find ${this.projectPath} -name "*.yml" -o -name "*.yaml" | grep -E "(playbook|play)" | head -20`); result.playbooks = playbooks.trim().split('\n').filter(Boolean); } catch { result.playbooks = []; } } if (queryLower.includes('role')) { try { const { stdout: roles } = await this.execAsync(`find ${this.projectPath} -type d -name "roles" | head -10`); result.roles = roles.trim().split('\n').filter(Boolean); } catch { result.roles = []; } } return result; } catch (_error) { return { error: 'Failed to query Ansible', details: _error.message }; } }, }; await this.registerCapability(capability); } /** * Register a capability if it's available */ async registerCapability(capability) { try { const isAvailable = await capability.detector(); if (isAvailable) { this.capabilities.set(capability.name, capability); // Update metadata if (capability.metadata) { capability.metadata.available = true; } this.logger.info(`✅ Capability registered: ${capability.name}`, 'EnvironmentCapabilityRegistry'); } else { this.logger.debug(`❌ Capability not available: ${capability.name}`, 'EnvironmentCapabilityRegistry'); } } catch { this.logger.warn(`Failed to register capability: ${capability.name}`, 'EnvironmentCapabilityRegistry'); } } /** * Query capabilities for research question */ async query(question) { const results = []; // Match capabilities to question const relevantCapabilities = this.matchCapabilities(question); for (const capability of relevantCapabilities) { try { const data = await capability.executor(question); results.push({ capability: capability.name, found: data && Object.keys(data).length > 0 && !data.error, data, confidence: this.calculateCapabilityConfidence(data), timestamp: new Date().toISOString(), }); } catch { this.logger.warn(`Failed to query capability: ${capability.name}`, 'EnvironmentCapabilityRegistry'); } } return results; } /** * Match capabilities to question */ matchCapabilities(question) { const questionLower = question.toLowerCase(); const matched = []; for (const capability of this.capabilities.values()) { // Check if question mentions capability name if (questionLower.includes(capability.name) || questionLower.includes(capability.type)) { matched.push(capability); continue; } // Check for related keywords const keywords = this.getCapabilityKeywords(capability.type); if (keywords.some(keyword => questionLower.includes(keyword))) { matched.push(capability); } } return matched; } /** * Get keywords for capability type */ getCapabilityKeywords(type) { const keywordMap = { kubernetes: ['k8s', 'pod', 'deployment', 'service', 'cluster'], openshift: ['ocp', 'route', 'buildconfig', 'project', 'build'], docker: ['container', 'image', 'volume'], podman: ['pod', 'container', 'image'], ansible: ['playbook', 'inventory', 'role', 'automation'], os: ['system', 'cpu', 'memory', 'disk', 'network', 'os', 'platform'], }; return keywordMap[type] || []; } /** * Calculate confidence for capability data */ calculateCapabilityConfidence(data) { if (!data || data.error) return 0; const keys = Object.keys(data); if (keys.length === 0) return 0; // Higher confidence for more data points return Math.min(0.5 + keys.length * 0.1, 0.95); } /** * List all available capabilities */ listCapabilities() { return Array.from(this.capabilities.keys()); } /** * Check if capability is available */ has(capabilityName) { return this.capabilities.has(capabilityName); } /** * Get capability metadata */ getCapabilityMetadata(capabilityName) { const capability = this.capabilities.get(capabilityName); return capability?.metadata || null; } } //# sourceMappingURL=environment-capability-registry.js.map