Skip to main content
Glama
cbunting99

MCP Code Analysis & Quality Server

by cbunting99
ConfigParser.ts10.3 kB
// Copyright 2025 Chris Bunting // Brief: Configuration file parser for Static Analysis MCP Server // Scope: Parses configuration files for various static analysis tools import { readFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import * as YAML from 'yaml'; import * as TOML from 'toml'; import * as xml2js from 'xml2js'; import { Language } from '@mcp-code-analysis/shared-types'; export interface ConfigFile { path: string; language: Language; format: ConfigFormat; content: any; } export enum ConfigFormat { JSON = 'json', YAML = 'yaml', TOML = 'toml', XML = 'xml', INI = 'ini', PROPERTIES = 'properties' } export class ConfigParser { private configFiles: Map<Language, string[]> = new Map([ [Language.JAVASCRIPT, [ '.eslintrc', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml', '.eslintrc.js', 'package.json', '.jshintrc' ]], [Language.TYPESCRIPT, [ 'tsconfig.json', '.eslintrc', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml', '.eslintrc.js', 'package.json' ]], [Language.PYTHON, [ 'pylintrc', '.pylintrc', 'setup.cfg', 'pyproject.toml', 'tox.ini', '.flake8' ]], [Language.JAVA, [ 'pom.xml', 'build.gradle', 'build.gradle.kts', 'checkstyle.xml', 'pmd.xml', 'spotbugs.xml', '.checkstyle' ]], [Language.C, [ '.clang-format', '.clang-tidy', 'Makefile', 'CMakeLists.txt' ]], [Language.CPP, [ '.clang-format', '.clang-tidy', 'Makefile', 'CMakeLists.txt', 'compile_commands.json' ]], [Language.GO, [ '.golangci.yml', '.golangci.yaml', '.golangci-lint.yml', '.golangci-lint.yaml', 'go.mod' ]], [Language.RUST, [ 'Cargo.toml', '.clippy.toml', 'rustfmt.toml', '.rustfmt.toml' ]] ]); async findConfigFiles(projectPath: string, language?: Language): Promise<ConfigFile[]> { const configFiles: ConfigFile[] = []; const languages = language ? [language] : Array.from(this.configFiles.keys()); for (const lang of languages) { const configFileNames = this.configFiles.get(lang) || []; for (const configFileName of configFileNames) { const configPath = join(projectPath, configFileName); if (existsSync(configPath)) { try { const configFile = await this.parseConfigFile(configPath, lang); configFiles.push(configFile); } catch (error) { // Skip invalid config files console.warn(`Failed to parse config file ${configPath}:`, error); } } } } return configFiles; } async parseConfigFile(filePath: string, language: Language): Promise<ConfigFile> { const content = readFileSync(filePath, 'utf-8'); const format = this.detectConfigFormat(filePath, content); const parsedContent = await this.parseContent(content, format); return { path: filePath, language, format, content: parsedContent }; } private detectConfigFormat(filePath: string, content: string): ConfigFormat { const extension = filePath.split('.').pop()?.toLowerCase(); const basename = filePath.split('/').pop() || ''; switch (extension) { case 'json': return ConfigFormat.JSON; case 'yaml': case 'yml': return ConfigFormat.YAML; case 'toml': return ConfigFormat.TOML; case 'xml': return ConfigFormat.XML; case 'ini': return ConfigFormat.INI; case 'properties': return ConfigFormat.PROPERTIES; default: // Try to detect by content if (content.trim().startsWith('{')) { return ConfigFormat.JSON; } else if (content.trim().startsWith('---')) { return ConfigFormat.YAML; } else if (content.includes(' = ')) { return ConfigFormat.TOML; } else if (content.trim().startsWith('<')) { return ConfigFormat.XML; } else if (content.includes('[') && content.includes(']')) { return ConfigFormat.INI; } return ConfigFormat.JSON; // Default fallback } } private async parseContent(content: string, format: ConfigFormat): Promise<any> { switch (format) { case ConfigFormat.JSON: return JSON.parse(content); case ConfigFormat.YAML: return YAML.parse(content); case ConfigFormat.TOML: return TOML.parse(content); case ConfigFormat.XML: return new Promise((resolve, reject) => { xml2js.parseString(content, (err, result) => { if (err) { reject(err); } else { resolve(result); } }); }); case ConfigFormat.INI: return this.parseIni(content); case ConfigFormat.PROPERTIES: return this.parseProperties(content); default: throw new Error(`Unsupported config format: ${format}`); } } private parseIni(content: string): any { const result: any = {}; const lines = content.split('\n'); let currentSection = ''; for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine.startsWith('[') && trimmedLine.endsWith(']')) { currentSection = trimmedLine.slice(1, -1); result[currentSection] = result[currentSection] || {}; } else if (trimmedLine && !trimmedLine.startsWith(';') && !trimmedLine.startsWith('#')) { const [key, ...valueParts] = trimmedLine.split('='); if (key && valueParts.length > 0) { const value = valueParts.join('=').trim(); if (currentSection) { result[currentSection][key.trim()] = value; } else { result[key.trim()] = value; } } } } return result; } private parseProperties(content: string): any { const result: any = {}; const lines = content.split('\n'); for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine && !trimmedLine.startsWith('#') && !trimmedLine.startsWith('!')) { const [key, ...valueParts] = trimmedLine.split('='); if (key && valueParts.length > 0) { const value = valueParts.join('=').trim(); result[key.trim()] = value; } } } return result; } extractESLintConfig(configFile: ConfigFile): any { if (configFile.language === Language.JAVASCRIPT || configFile.language === Language.TYPESCRIPT) { if (configFile.path.endsWith('package.json')) { return configFile.content.eslintConfig || {}; } return configFile.content; } return {}; } extractPylintConfig(configFile: ConfigFile): any { if (configFile.language === Language.PYTHON) { if (configFile.format === ConfigFormat.INI) { return configFile.content; } else if (configFile.format === ConfigFormat.TOML && configFile.content.tool?.pylint) { return configFile.content.tool.pylint; } } return {}; } extractJavaConfig(configFile: ConfigFile): any { if (configFile.language === Language.JAVA) { if (configFile.path.endsWith('pom.xml')) { return this.extractMavenConfig(configFile.content); } else if (configFile.path.includes('gradle')) { return this.extractGradleConfig(configFile.content); } } return {}; } private extractMavenConfig(pomContent: any): any { const config: any = {}; if (pomContent.project?.properties) { config.maven = pomContent.project.properties; } if (pomContent.project?.build?.plugins) { const plugins = Array.isArray(pomContent.project.build.plugins.plugin) ? pomContent.project.build.plugins.plugin : [pomContent.project.build.plugins.plugin]; config.plugins = plugins.map((plugin: any) => ({ groupId: plugin.groupId, artifactId: plugin.artifactId, version: plugin.version, configuration: plugin.configuration })); } return config; } private extractGradleConfig(gradleContent: any): any { // This is a simplified extraction - in practice, Gradle files are Groovy/Kotlin scripts // and would need more sophisticated parsing return { gradle: gradleContent }; } mergeConfigs(configFiles: ConfigFile[]): any { const merged: any = {}; for (const configFile of configFiles) { const languageConfig = this.extractLanguageConfig(configFile); Object.assign(merged, languageConfig); } return merged; } private extractLanguageConfig(configFile: ConfigFile): any { switch (configFile.language) { case Language.JAVASCRIPT: case Language.TYPESCRIPT: return this.extractESLintConfig(configFile); case Language.PYTHON: return this.extractPylintConfig(configFile); case Language.JAVA: return this.extractJavaConfig(configFile); default: return configFile.content; } } validateConfig(configFile: ConfigFile): boolean { try { switch (configFile.language) { case Language.JAVASCRIPT: case Language.TYPESCRIPT: return this.validateESLintConfig(configFile); case Language.PYTHON: return this.validatePylintConfig(configFile); case Language.JAVA: return this.validateJavaConfig(configFile); default: return true; } } catch (error) { return false; } } private validateESLintConfig(configFile: ConfigFile): boolean { const config = this.extractESLintConfig(configFile); return typeof config === 'object' && config !== null; } private validatePylintConfig(configFile: ConfigFile): boolean { const config = this.extractPylintConfig(configFile); return typeof config === 'object' && config !== null; } private validateJavaConfig(configFile: ConfigFile): boolean { const config = this.extractJavaConfig(configFile); return typeof config === 'object' && config !== null; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cbunting99/mcp-code-analysis-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server