mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
489 lines • 20.5 kB
JavaScript
/**
* 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