calculate-metrics
Analyze code to calculate metrics like complexity, maintainability, and lines of code for quality assessment and improvement.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| repositoryUrl | No | URL of the repository to analyze (e.g., 'https://github.com/username/repo') | |
| filePath | No | Path to the file within the repository (e.g., 'src/main.ts') | |
| fileContent | No | Source code content to analyze directly instead of from a repository | |
| language | No | Programming language of the code (e.g., 'javascript', 'python', 'typescript', 'rust') | |
| metrics | No | Specific metrics to calculate, such as 'complexity', 'linesOfCode', 'maintainability', 'functions', 'classes' |
Implementation Reference
- src/features/basic-analysis/index.ts:66-101 (registration)Registration of the 'calculate-metrics' MCP tool, including input schema (Zod) and inline handler function that delegates to getMetrics and formats the MCP response.server.tool( "calculate-metrics", { repositoryUrl: z.string().optional().describe("URL of the repository to analyze (e.g., 'https://github.com/username/repo')"), filePath: z.string().optional().describe("Path to the file within the repository (e.g., 'src/main.ts')"), fileContent: z.string().optional().describe("Source code content to analyze directly instead of from a repository"), language: z.string().optional().describe("Programming language of the code (e.g., 'javascript', 'python', 'typescript', 'rust')"), metrics: z.array(z.string()).optional().describe("Specific metrics to calculate, such as 'complexity', 'linesOfCode', 'maintainability', 'functions', 'classes'") }, async ({ repositoryUrl, filePath, fileContent, language, metrics }) => { try { const results = await getMetrics({ repositoryUrl, filePath, fileContent, language, metrics: metrics || ["complexity", "linesOfCode", "maintainability"] }); return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: `Error calculating metrics: ${(error as Error).message}` }], isError: true }; } } );
- getMetrics helper function called by the tool handler. Handles caching, repository/file analysis orchestration, reads code, and delegates to calculateMetrics.export async function getMetrics(options: { repositoryUrl?: string; filePath?: string; fileContent?: string; language?: string; metrics?: string[]; analysisId?: string; type?: string; }): Promise<any> { const { repositoryUrl, filePath, fileContent, language, metrics, analysisId, type } = options; // If analysisId is provided, retrieve from cache if (analysisId) { const cachedAnalysis = analysisCache.get(analysisId); if (!cachedAnalysis) { throw new Error(`Analysis not found: ${analysisId}`); } if (type) { return cachedAnalysis[type] || {}; } return cachedAnalysis; } // Otherwise perform new analysis if (repositoryUrl) { const repoPath = await getRepository(repositoryUrl); if (filePath) { // Analyze specific file const fullPath = path.join(repoPath, filePath); const code = fs.readFileSync(fullPath, 'utf8'); return calculateMetrics(code, language, metrics); } else { // Analyze entire repository const files = listFiles(repoPath); const allMetrics: Record<string, any> = {}; for (const file of files) { const fullPath = path.join(repoPath, file); const code = fs.readFileSync(fullPath, 'utf8'); allMetrics[file] = calculateMetrics(code, path.extname(file).slice(1), metrics); } return allMetrics; } } else if (fileContent) { // Analyze provided code content return calculateMetrics(fileContent, language, metrics); } else { throw new Error("Either repositoryUrl, filePath, or fileContent must be provided"); } }
- Core calculateMetrics function implementing the metric computations: linesOfCode, complexity (cyclomatic-like), maintainability score.function calculateMetrics(code: string, language?: string, metricTypes?: string[]): Record<string, any> { const result: Record<string, any> = {}; if (!metricTypes || metricTypes.includes('linesOfCode')) { result.linesOfCode = code.split('\n').length; } if (!metricTypes || metricTypes.includes('complexity')) { // Simple complexity heuristic (would be replaced with actual analysis) const decisions = (code.match(/if|else|for|while|switch|case|try|catch/g) || []).length; result.complexity = decisions; } if (!metricTypes || metricTypes.includes('maintainability')) { // Simple maintainability heuristic const commentLines = (code.match(/\/\/.*$|\/*[\s\S]*?\*\//gm) || []).length; const codeLines = code.split('\n').length; // Calculate a simple maintainability index (higher is better) const commentRatio = commentLines / codeLines; const complexity = result.complexity || 0; result.maintainability = Math.max(0, 100 - (complexity * 0.25) + (commentRatio * 20)); } return result; }