compare_results
Compare two Semgrep scan result files (old and new JSON) to detect changes in findings, track issue resolution, and monitor code quality improvements over time.
Instructions
Compares two scan results
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| old_results | Yes | Absolute path to older JSON results file | |
| new_results | Yes | Absolute path to newer JSON results file |
Implementation Reference
- src/index.ts:689-761 (handler)Main handler for the 'compare_results' tool. Reads old and new JSON scan result files, compares findings using a composite key (check_id:path:line:col), and returns added/removed/unchanged findings with a summary.
private async handleCompareResults(args: any) { if (!args.old_results || !args.new_results) { throw new McpError( ErrorCode.InvalidParams, 'old_results and new_results are required' ); } const oldResultsFile = validateAbsolutePath(args.old_results, 'old_results'); const newResultsFile = validateAbsolutePath(args.new_results, 'new_results'); try { const [oldContent, newContent] = await Promise.all([ readFile(oldResultsFile, 'utf-8'), readFile(newResultsFile, 'utf-8'), ]); const oldResults = getSemgrepFindings(parseSemgrepResults(oldContent)); const newResults = getSemgrepFindings(parseSemgrepResults(newContent)); const oldFindings = new Set(oldResults.map((r: any) => `${r.check_id}:${r.path}:${r.start?.line}:${r.start?.col}` )); const comparison = { total_old: oldResults.length, total_new: newResults.length, added: [] as any[], removed: [] as any[], unchanged: [] as any[] }; newResults.forEach((finding: any) => { const key = `${finding.check_id}:${finding.path}:${finding.start?.line}:${finding.start?.col}`; if (oldFindings.has(key)) { comparison.unchanged.push(finding); } else { comparison.added.push(finding); } }); const newKeys = new Set(newResults.map((r: any) => `${r.check_id}:${r.path}:${r.start?.line}:${r.start?.col}` )); oldResults.forEach((finding: any) => { const key = `${finding.check_id}:${finding.path}:${finding.start?.line}:${finding.start?.col}`; if (!newKeys.has(key)) { comparison.removed.push(finding); } }); return { content: [{ type: 'text', text: JSON.stringify({ summary: { old_findings: comparison.total_old, new_findings: comparison.total_new, added: comparison.added.length, removed: comparison.removed.length, unchanged: comparison.unchanged.length }, details: comparison }, null, 2) }] }; } catch (error: any) { return { content: [{ type: 'text', text: `Error comparing results: ${error.message}` }], isError: true }; } } - src/index.ts:356-367 (schema)Input schema for 'compare_results' tool, defining required parameters 'old_results' and 'new_results' (both absolute file paths).
{ name: 'compare_results', description: 'Compares two scan results', inputSchema: { type: 'object', properties: { old_results: { type: 'string', description: 'Absolute path to older JSON results file' }, new_results: { type: 'string', description: 'Absolute path to newer JSON results file' } }, required: ['old_results', 'new_results'] } } - src/index.ts:371-395 (registration)The tool registration in the CallToolRequestSchema handler. Routes 'compare_results' to handleCompareResults method.
this.server.setRequestHandler(CallToolRequestSchema, async (request) => { await this.ensureSemgrepAvailable(); switch (request.params.name) { case 'scan_directory': return await this.handleScanDirectory(request.params.arguments); case 'list_rules': return await this.handleListRules(request.params.arguments); case 'analyze_results': return await this.handleAnalyzeResults(request.params.arguments); case 'create_rule': return await this.handleCreateRule(request.params.arguments); case 'filter_results': return await this.handleFilterResults(request.params.arguments); case 'export_results': return await this.handleExportResults(request.params.arguments); case 'compare_results': return await this.handleCompareResults(request.params.arguments); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } });