Skip to main content
Glama

CodeAnalysis MCP Server

by 0xjcf
dependency-analyzer.ts10.8 kB
import fs from 'fs/promises'; import path from 'path'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); interface DependencyNode { name: string; version?: string; type: 'direct' | 'dev' | 'peer' | 'internal' | 'external'; path?: string; } interface DependencyEdge { source: string; target: string; type: 'imports' | 'requires' | 'uses'; } interface DependencyGraph { nodes: DependencyNode[]; edges: DependencyEdge[]; } /** * Analyze dependencies in a repository */ export async function analyzeDependencies(repositoryPath: string): Promise<{ graph: DependencyGraph; summary: { totalDependencies: number; directDependencies: number; devDependencies: number; internalDependencies: number; }; }> { if (!repositoryPath) { throw new Error("Repository path is required"); } // Detect project type const projectType = await detectProjectType(repositoryPath); let dependencyGraph: DependencyGraph; // Analyze based on project type switch (projectType) { case 'node': dependencyGraph = await analyzeNodeDependencies(repositoryPath); break; case 'python': dependencyGraph = await analyzePythonDependencies(repositoryPath); break; case 'java': dependencyGraph = await analyzeJavaDependencies(repositoryPath); break; default: dependencyGraph = await analyzeGenericDependencies(repositoryPath); } // Generate summary const summary = { totalDependencies: dependencyGraph.nodes.length, directDependencies: dependencyGraph.nodes.filter(n => n.type === 'direct').length, devDependencies: dependencyGraph.nodes.filter(n => n.type === 'dev').length, internalDependencies: dependencyGraph.nodes.filter(n => n.type === 'internal').length }; return { graph: dependencyGraph, summary }; } /** * Detect the type of project in the repository */ async function detectProjectType(repositoryPath: string): Promise<string> { try { // Check for package.json (Node.js) if (await fileExists(path.join(repositoryPath, 'package.json'))) { return 'node'; } // Check for requirements.txt or setup.py (Python) if ( await fileExists(path.join(repositoryPath, 'requirements.txt')) || await fileExists(path.join(repositoryPath, 'setup.py')) ) { return 'python'; } // Check for pom.xml or build.gradle (Java) if ( await fileExists(path.join(repositoryPath, 'pom.xml')) || await fileExists(path.join(repositoryPath, 'build.gradle')) ) { return 'java'; } // Default to generic return 'generic'; } catch (error) { console.error('Error detecting project type:', error); return 'generic'; } } /** * Analyze Node.js dependencies */ async function analyzeNodeDependencies(repositoryPath: string): Promise<DependencyGraph> { const nodes: DependencyNode[] = []; const edges: DependencyEdge[] = []; try { // Read package.json to extract dependencies const packageJsonPath = path.join(repositoryPath, 'package.json'); const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8'); const packageJson = JSON.parse(packageJsonContent); // Add direct dependencies if (packageJson.dependencies) { for (const [name, version] of Object.entries(packageJson.dependencies)) { nodes.push({ name, version: version as string, type: 'direct' }); } } // Add dev dependencies if (packageJson.devDependencies) { for (const [name, version] of Object.entries(packageJson.devDependencies)) { nodes.push({ name, version: version as string, type: 'dev' }); } } // Find all JS/TS files const files = await findFiles(repositoryPath, ['.js', '.ts', '.jsx', '.tsx']); // Analyze imports in each file for (const file of files) { const fullPath = path.join(repositoryPath, file); const content = await fs.readFile(fullPath, 'utf8'); // Extract imports const imports = extractImports(content); for (const importPath of imports) { if (importPath.startsWith('.')) { // Internal file import const targetPath = path.resolve(path.dirname(fullPath), importPath); const relativePath = path.relative(repositoryPath, targetPath); // Add node if it doesn't exist if (!nodes.some(n => n.path === relativePath)) { nodes.push({ name: relativePath, type: 'internal', path: relativePath }); } // Add edge edges.push({ source: file, target: relativePath, type: 'imports' }); } else { // External package import const packageName = importPath.split('/')[0]; // Add edge to the dependency edges.push({ source: file, target: packageName, type: 'imports' }); } } } return { nodes, edges }; } catch (error) { console.error('Error analyzing Node.js dependencies:', error); return { nodes: [], edges: [] }; } } /** * Analyze Python dependencies */ async function analyzePythonDependencies(repositoryPath: string): Promise<DependencyGraph> { const nodes: DependencyNode[] = []; const edges: DependencyEdge[] = []; try { // Read requirements.txt if it exists const requirementsPath = path.join(repositoryPath, 'requirements.txt'); if (await fileExists(requirementsPath)) { const content = await fs.readFile(requirementsPath, 'utf8'); const lines = content.split('\n'); for (const line of lines) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#')) { // Extract package name and version const parts = trimmed.split('=='); const name = parts[0].trim(); const version = parts.length > 1 ? parts[1].trim() : undefined; nodes.push({ name, version, type: 'direct' }); } } } // Find all Python files const files = await findFiles(repositoryPath, ['.py']); // Analyze imports in each file for (const file of files) { const fullPath = path.join(repositoryPath, file); const content = await fs.readFile(fullPath, 'utf8'); // Extract Python imports const lines = content.split('\n'); for (const line of lines) { const importMatch = line.match(/^\s*import\s+([a-zA-Z0-9_,.]+)/) || line.match(/^\s*from\s+([a-zA-Z0-9_.]+)\s+import/); if (importMatch) { const moduleName = importMatch[1].split('.')[0]; // Add edge edges.push({ source: file, target: moduleName, type: 'imports' }); } } } return { nodes, edges }; } catch (error) { console.error('Error analyzing Python dependencies:', error); return { nodes: [], edges: [] }; } } /** * Analyze Java dependencies */ async function analyzeJavaDependencies(repositoryPath: string): Promise<DependencyGraph> { // Simplified implementation for Java const nodes: DependencyNode[] = []; const edges: DependencyEdge[] = []; // In a real implementation, you would parse pom.xml or build.gradle // and analyze import statements in Java files return { nodes, edges }; } /** * Generic dependency analysis for any codebase */ async function analyzeGenericDependencies(repositoryPath: string): Promise<DependencyGraph> { const nodes: DependencyNode[] = []; const edges: DependencyEdge[] = []; try { // Find all code files const fileExtensions = ['.js', '.ts', '.py', '.java', '.c', '.cpp', '.go', '.rb', '.php']; const files = await findFiles(repositoryPath, fileExtensions); // Add each file as a node for (const file of files) { nodes.push({ name: file, type: 'internal', path: file }); } // Simple content-based analysis to detect potential dependencies for (const file of files) { const fullPath = path.join(repositoryPath, file); const content = await fs.readFile(fullPath, 'utf8'); // Check for references to other files for (const otherFile of files) { if (file !== otherFile) { const otherFileName = path.basename(otherFile); if (content.includes(otherFileName)) { edges.push({ source: file, target: otherFile, type: 'uses' }); } } } } return { nodes, edges }; } catch (error) { console.error('Error analyzing generic dependencies:', error); return { nodes: [], edges: [] }; } } /** * Extract imports from JavaScript/TypeScript code */ function extractImports(content: string): string[] { const imports: string[] = []; // Match ES6 imports const es6Regex = /import\s+(?:.+\s+from\s+)?['"]([^'"]+)['"]/g; let match; while ((match = es6Regex.exec(content)) !== null) { imports.push(match[1]); } // Match CommonJS requires const cjsRegex = /require\(['"]([^'"]+)['"]\)/g; while ((match = cjsRegex.exec(content)) !== null) { imports.push(match[1]); } return imports; } /** * Find files with specific extensions in a directory */ async function findFiles(dir: string, extensions: string[]): Promise<string[]> { const files: string[] = []; async function scanDir(currentDir: string, relativePath: string = '') { const entries = await fs.readdir(currentDir, { withFileTypes: true }); for (const entry of entries) { const entryRelativePath = path.join(relativePath, entry.name); const entryPath = path.join(currentDir, entry.name); if (entry.isDirectory()) { // Skip node_modules and other common excluded directories if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) { await scanDir(entryPath, entryRelativePath); } } else if (extensions.some(ext => entry.name.endsWith(ext))) { files.push(entryRelativePath); } } } await scanDir(dir); return files; } /** * Check if a file exists */ async function fileExists(filePath: string): Promise<boolean> { try { await fs.access(filePath); return true; } catch { return false; } }

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/0xjcf/MCP_CodeAnalysis'

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