import fs from 'fs/promises';
import path from 'path';
import os from 'os';
/**
* Analyzes MCP server configurations to identify servers and their settings
*/
export class MCPConfigurationAnalyzer {
constructor() {
this.configPaths = [
path.join(os.homedir(), '.config', 'claude-code', 'mcp.json'),
path.join(os.homedir(), '.config', 'claude-desktop', 'mcp.json'),
path.join(process.cwd(), '.mcp.json'),
path.join(os.homedir(), '.claude', 'mcp.json'),
path.join(os.homedir(), '.config', 'claude', 'mcp.json'),
path.join(os.homedir(), '.mcp.json')
];
}
/**
* Find and parse MCP configuration files
* @returns {Promise<Object>} Parsed configuration with server details
*/
async analyzeConfiguration() {
const results = {
configPath: null,
servers: [],
totalServers: 0,
hasConfiguration: false
};
// Try to find configuration file
for (const configPath of this.configPaths) {
try {
const configContent = await fs.readFile(configPath, 'utf8');
const config = JSON.parse(configContent);
results.configPath = configPath;
results.hasConfiguration = true;
// Extract server information
if (config.mcpServers) {
results.servers = Object.entries(config.mcpServers).map(([name, serverConfig]) => ({
name,
command: serverConfig.command,
args: serverConfig.args || [],
env: serverConfig.env || {},
disabled: serverConfig.disabled || false,
configType: this.detectConfigType(serverConfig)
}));
}
results.totalServers = results.servers.length;
break;
} catch (error) {
// Continue to next config path
continue;
}
}
return results;
}
/**
* Detect the type of MCP server configuration
* @param {Object} serverConfig Server configuration object
* @returns {string} Configuration type
*/
detectConfigType(serverConfig) {
if (serverConfig.command && serverConfig.command.includes('npm')) {
return 'npm';
}
if (serverConfig.command && serverConfig.command.includes('python')) {
return 'python';
}
if (serverConfig.command && serverConfig.command.includes('node')) {
return 'node';
}
return 'unknown';
}
/**
* Get active (enabled) servers only
* @returns {Promise<Array>} Array of active server configurations
*/
async getActiveServers() {
const config = await this.analyzeConfiguration();
return config.servers.filter(server => !server.disabled);
}
/**
* Get configuration summary for reporting
* @returns {Promise<Object>} Summary statistics
*/
async getConfigurationSummary() {
const config = await this.analyzeConfiguration();
const activeServers = config.servers.filter(server => !server.disabled);
return {
configurationFound: config.hasConfiguration,
configPath: config.configPath,
totalServers: config.totalServers,
activeServers: activeServers.length,
disabledServers: config.totalServers - activeServers.length,
serverTypes: this.groupServersByType(config.servers)
};
}
/**
* Group servers by their configuration type
* @param {Array} servers Array of server configurations
* @returns {Object} Servers grouped by type
*/
groupServersByType(servers) {
return servers.reduce((acc, server) => {
acc[server.configType] = (acc[server.configType] || 0) + 1;
return acc;
}, {});
}
/**
* Validate server configuration
* @param {Object} serverConfig Server configuration to validate
* @returns {Object} Validation results
*/
validateServerConfig(serverConfig) {
const issues = [];
if (!serverConfig.command) {
issues.push('Missing command');
}
if (serverConfig.command && !this.isCommandAccessible(serverConfig.command)) {
issues.push('Command not found in PATH');
}
return {
valid: issues.length === 0,
issues
};
}
/**
* Check if a command is accessible in the system PATH
* @param {string} command Command to check
* @returns {boolean} Whether command is accessible
*/
isCommandAccessible(command) {
// Extract base command (first word)
const baseCommand = command.split(' ')[0];
// Common commands that should be available
const commonCommands = ['node', 'npm', 'python', 'python3', 'npx'];
return commonCommands.includes(baseCommand);
}
}