Skip to main content
Glama
haasonsaas

Deep Code Reasoning MCP Server

by haasonsaas
HypothesisTester.ts22.2 kB
import * as fs from 'fs/promises'; import type { CodeScope, CodeLocation, Hypothesis, } from '../models/types.js'; interface TestResult extends Hypothesis { confidence: number; supportingEvidence: Evidence[]; contradictingEvidence: Evidence[]; suggestedFix?: string; } interface Evidence { type: 'code_pattern' | 'execution_trace' | 'data_flow' | 'timing'; description: string; location: CodeLocation; strength: 'weak' | 'moderate' | 'strong'; } interface TestStrategy { name: string; applicableHypotheses: string[]; execute: (hypothesis: string, scope: CodeScope) => Promise<Evidence[]>; } export class HypothesisTester { private testStrategies: Map<string, TestStrategy>; constructor() { this.testStrategies = new Map(); this.initializeStrategies(); } async testHypothesis( hypothesis: string, codeScope: CodeScope, testApproach: string, ): Promise<TestResult> { const hypothesisId = `hyp_${Date.now()}`; // Select appropriate test strategies const strategies = this.selectStrategies(hypothesis, testApproach); // Gather evidence const allEvidence: Evidence[] = []; for (const strategy of strategies) { try { const evidence = await strategy.execute(hypothesis, codeScope); allEvidence.push(...evidence); } catch (error) { console.error(`Strategy ${strategy.name} failed:`, error); } } // Analyze evidence const supportingEvidence = allEvidence.filter(e => this.supportsHypothesis(e, hypothesis)); const contradictingEvidence = allEvidence.filter(e => this.contradictsHypothesis(e, hypothesis)); // Calculate confidence const confidence = this.calculateConfidence(supportingEvidence, contradictingEvidence); // Determine validation status const validated = confidence > 0.7 && supportingEvidence.length > contradictingEvidence.length; // Generate fix suggestion if validated const suggestedFix = validated ? this.generateFixSuggestion(hypothesis, supportingEvidence) : undefined; return { id: hypothesisId, description: hypothesis, testApproach, validated, evidence: supportingEvidence.map(e => e.description), confidence, supportingEvidence, contradictingEvidence, suggestedFix, }; } private initializeStrategies() { // Race condition detection strategy this.testStrategies.set('race_condition', { name: 'race_condition', applicableHypotheses: ['race', 'concurrent', 'async', 'timing'], execute: async (hypothesis, scope) => this.testRaceCondition(hypothesis, scope), }); // N+1 query detection strategy this.testStrategies.set('n_plus_one', { name: 'n_plus_one', applicableHypotheses: ['n+1', 'query', 'database', 'performance'], execute: async (hypothesis, scope) => this.testNPlusOne(hypothesis, scope), }); // Memory leak detection strategy this.testStrategies.set('memory_leak', { name: 'memory_leak', applicableHypotheses: ['memory', 'leak', 'allocation', 'cleanup'], execute: async (hypothesis, scope) => this.testMemoryLeak(hypothesis, scope), }); // State corruption detection strategy this.testStrategies.set('state_corruption', { name: 'state_corruption', applicableHypotheses: ['state', 'corruption', 'mutation', 'shared'], execute: async (hypothesis, scope) => this.testStateCorruption(hypothesis, scope), }); // Deadlock detection strategy this.testStrategies.set('deadlock', { name: 'deadlock', applicableHypotheses: ['deadlock', 'lock', 'mutex', 'synchronization'], execute: async (hypothesis, scope) => this.testDeadlock(hypothesis, scope), }); } private selectStrategies(hypothesis: string, testApproach: string): TestStrategy[] { const strategies: TestStrategy[] = []; const hypothesisLower = hypothesis.toLowerCase(); // Select based on hypothesis keywords for (const [_name, strategy] of this.testStrategies) { if (strategy.applicableHypotheses.some(keyword => hypothesisLower.includes(keyword))) { strategies.push(strategy); } } // If no specific strategies found, use general approach if (strategies.length === 0) { if (testApproach.includes('trace')) { strategies.push(this.createGeneralTraceStrategy()); } else if (testApproach.includes('pattern')) { strategies.push(this.createGeneralPatternStrategy()); } } return strategies; } private async testRaceCondition(hypothesis: string, scope: CodeScope): Promise<Evidence[]> { const evidence: Evidence[] = []; for (const file of scope.files) { const content = await this.readFile(file); // Look for async operations without proper synchronization const asyncPatterns = [ /async\s+function/g, /\.then\(/g, /await\s+/g, /Promise\./g, ]; const sharedStatePatterns = [ /this\.\w+\s*=/g, /global\.\w+\s*=/g, /window\.\w+\s*=/g, ]; // Check for shared state access in async contexts const asyncMatches = this.findMatches(content, asyncPatterns); const stateMatches = this.findMatches(content, sharedStatePatterns); // Look for potential race conditions for (const asyncMatch of asyncMatches) { const nearbyStateAccess = stateMatches.filter(stateMatch => Math.abs(stateMatch.line - asyncMatch.line) < 10, ); if (nearbyStateAccess.length > 0) { evidence.push({ type: 'code_pattern', description: `Shared state access in async context at line ${asyncMatch.line}`, location: { file, line: asyncMatch.line, }, strength: 'moderate', }); } } // Look for missing locks/synchronization const lockPatterns = [/mutex/, /lock/, /synchronized/, /Semaphore/]; const hasLocks = lockPatterns.some(pattern => pattern.test(content)); if (asyncMatches.length > 2 && !hasLocks) { evidence.push({ type: 'code_pattern', description: 'Multiple async operations without synchronization primitives', location: { file, line: 0, }, strength: 'weak', }); } } return evidence; } private async testNPlusOne(hypothesis: string, scope: CodeScope): Promise<Evidence[]> { const evidence: Evidence[] = []; for (const file of scope.files) { const content = await this.readFile(file); const lines = content.split('\n'); // Look for queries inside loops let inLoop = false; let loopDepth = 0; let _loopStartLine = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Track loop entry/exit if (this.isLoopStart(line)) { if (!inLoop) _loopStartLine = i; inLoop = true; loopDepth++; } else if (line.includes('}') && inLoop) { loopDepth--; if (loopDepth === 0) inLoop = false; } // Check for queries inside loops if (inLoop) { const queryPatterns = [ /\.(find|findOne|findAll|select)\(/, /query\(/, /SELECT/i, /fetch\(/, ]; if (queryPatterns.some(pattern => pattern.test(line))) { evidence.push({ type: 'code_pattern', description: `Database query inside loop at line ${i + 1}`, location: { file, line: i + 1, }, strength: 'strong', }); } } } // Look for missing eager loading const modelPatterns = /\.(hasMany|belongsTo|hasOne)\(/g; const includePatterns = /include:|includes:/g; const hasRelations = modelPatterns.test(content); const hasIncludes = includePatterns.test(content); if (hasRelations && !hasIncludes) { evidence.push({ type: 'code_pattern', description: 'Model relations defined without eager loading', location: { file, line: 0, }, strength: 'moderate', }); } } return evidence; } private async testMemoryLeak(hypothesis: string, scope: CodeScope): Promise<Evidence[]> { const evidence: Evidence[] = []; for (const file of scope.files) { const content = await this.readFile(file); // Check for uncleared timers const timerSetters = [ { pattern: /setInterval\(/g, cleanup: /clearInterval/ }, { pattern: /setTimeout\(/g, cleanup: /clearTimeout/ }, ]; for (const { pattern, cleanup } of timerSetters) { const setMatches = [...content.matchAll(pattern)]; const hasCleanup = cleanup.test(content); if (setMatches.length > 0 && !hasCleanup) { evidence.push({ type: 'code_pattern', description: 'Timer set without corresponding cleanup', location: { file, line: this.getLineNumber(content, setMatches[0].index || 0), }, strength: 'strong', }); } } // Check for event listeners without removal const listenerPattern = /addEventListener\(['"](\w+)['"]/g; const removePattern = /removeEventListener/; const listenerMatches = [...content.matchAll(listenerPattern)]; const hasRemoval = removePattern.test(content); if (listenerMatches.length > 0 && !hasRemoval) { evidence.push({ type: 'code_pattern', description: 'Event listeners added without removal', location: { file, line: this.getLineNumber(content, listenerMatches[0].index || 0), }, strength: 'moderate', }); } // Check for large object retention const largeObjectPatterns = [ /new\s+Array\(\d{4,}\)/g, // Arrays with 1000+ elements /new\s+Buffer\(/g, /cache\s*=\s*{/g, ]; for (const pattern of largeObjectPatterns) { const matches = [...content.matchAll(pattern)]; if (matches.length > 0) { // Check if objects are ever cleared const clearPatterns = [/= null/, /delete /, /\.clear\(/]; const hasClearing = clearPatterns.some(p => p.test(content)); if (!hasClearing) { evidence.push({ type: 'code_pattern', description: 'Large objects created without cleanup', location: { file, line: this.getLineNumber(content, matches[0].index || 0), }, strength: 'moderate', }); } } } } return evidence; } private async testStateCorruption(hypothesis: string, scope: CodeScope): Promise<Evidence[]> { const evidence: Evidence[] = []; for (const file of scope.files) { const content = await this.readFile(file); // Check for direct state mutation const mutationPatterns = [ /state\.\w+\s*=/g, /this\.state\.\w+\s*=/g, /\.push\(/g, /\.pop\(/g, /\.shift\(/g, /\.splice\(/g, ]; for (const pattern of mutationPatterns) { const matches = [...content.matchAll(pattern)]; for (const match of matches) { evidence.push({ type: 'code_pattern', description: 'Direct state mutation detected', location: { file, line: this.getLineNumber(content, match.index || 0), }, strength: 'strong', }); } } // Check for shared mutable state const sharedStatePatterns = [ /static\s+\w+\s*=\s*{/g, /global\.\w+\s*=\s*{/g, /window\.\w+\s*=\s*{/g, ]; for (const pattern of sharedStatePatterns) { const matches = [...content.matchAll(pattern)]; if (matches.length > 0) { evidence.push({ type: 'code_pattern', description: 'Shared mutable state detected', location: { file, line: this.getLineNumber(content, matches[0].index || 0), }, strength: 'moderate', }); } } // Check for missing defensive copying const returnPatterns = /return\s+this\.\w+(?!\.\w+)/g; const returnMatches = [...content.matchAll(returnPatterns)]; for (const match of returnMatches) { const line = this.getLineNumber(content, match.index || 0); const lineContent = content.split('\n')[line - 1]; // Check if it's returning an object/array without copying if (!lineContent.includes('...') && !lineContent.includes('Object.assign') && !lineContent.includes('.slice()')) { evidence.push({ type: 'code_pattern', description: 'Returning internal state without defensive copying', location: { file, line, }, strength: 'moderate', }); } } } return evidence; } private async testDeadlock(hypothesis: string, scope: CodeScope): Promise<Evidence[]> { const evidence: Evidence[] = []; for (const file of scope.files) { const content = await this.readFile(file); // Look for multiple lock acquisitions const lockPatterns = [ /lock\(/g, /acquire\(/g, /mutex\./g, /synchronized/g, ]; const lockAcquisitions: Array<{ type: string; line: number }> = []; for (const pattern of lockPatterns) { const matches = [...content.matchAll(pattern)]; for (const match of matches) { lockAcquisitions.push({ type: match[0], line: this.getLineNumber(content, match.index || 0), }); } } // Check for multiple locks in same function if (lockAcquisitions.length > 1) { // Group by proximity (within 20 lines) const groups: Array<typeof lockAcquisitions> = []; let currentGroup: typeof lockAcquisitions = [lockAcquisitions[0]]; for (let i = 1; i < lockAcquisitions.length; i++) { if (lockAcquisitions[i].line - lockAcquisitions[i-1].line < 20) { currentGroup.push(lockAcquisitions[i]); } else { if (currentGroup.length > 1) groups.push(currentGroup); currentGroup = [lockAcquisitions[i]]; } } if (currentGroup.length > 1) groups.push(currentGroup); for (const group of groups) { evidence.push({ type: 'code_pattern', description: `Multiple lock acquisitions in close proximity (lines ${group[0].line}-${group[group.length-1].line})`, location: { file, line: group[0].line, }, strength: 'strong', }); } } // Check for nested locks const lines = content.split('\n'); let lockDepth = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (lockPatterns.some(p => p.test(line))) { lockDepth++; if (lockDepth > 1) { evidence.push({ type: 'code_pattern', description: 'Nested lock acquisition detected', location: { file, line: i + 1, }, strength: 'strong', }); } } else if (line.includes('unlock') || line.includes('release')) { lockDepth = Math.max(0, lockDepth - 1); } } } return evidence; } private createGeneralTraceStrategy(): TestStrategy { return { name: 'general_trace', applicableHypotheses: ['*'], execute: async (hypothesis, scope) => { const evidence: Evidence[] = []; // Generic execution trace analysis for (const file of scope.files) { const content = await this.readFile(file); // Look for error-prone patterns const errorPatterns = [ { pattern: /catch\s*\(\s*\)/g, desc: 'Empty catch block' }, { pattern: /==\s*null/g, desc: 'Loose null check' }, { pattern: /eval\(/g, desc: 'Use of eval' }, ]; for (const { pattern, desc } of errorPatterns) { const matches = [...content.matchAll(pattern)]; for (const match of matches) { evidence.push({ type: 'code_pattern', description: desc, location: { file, line: this.getLineNumber(content, match.index || 0), }, strength: 'weak', }); } } } return evidence; }, }; } private createGeneralPatternStrategy(): TestStrategy { return { name: 'general_pattern', applicableHypotheses: ['*'], execute: async (hypothesis, scope) => { const evidence: Evidence[] = []; // Pattern matching based on hypothesis keywords const keywords = hypothesis.toLowerCase().split(/\s+/); for (const file of scope.files) { const content = await this.readFile(file); const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i].toLowerCase(); const matchedKeywords = keywords.filter(k => line.includes(k)); if (matchedKeywords.length >= 2) { evidence.push({ type: 'code_pattern', description: `Code matches hypothesis keywords: ${matchedKeywords.join(', ')}`, location: { file, line: i + 1, }, strength: 'weak', }); } } } return evidence; }, }; } private supportsHypothesis(evidence: Evidence, _hypothesis: string): boolean { // Evidence supports if it has moderate or strong strength return evidence.strength !== 'weak'; } private contradictsHypothesis(evidence: Evidence, _hypothesis: string): boolean { // Check if evidence description contains contradicting terms const contradictingTerms = ['not', 'no', 'without', 'missing', 'absent']; const hasContradiction = contradictingTerms.some(term => evidence.description.toLowerCase().includes(term), ); // Weak evidence that mentions contradicting terms contradicts the hypothesis return evidence.strength === 'weak' && hasContradiction; } private calculateConfidence( supporting: Evidence[], contradicting: Evidence[], ): number { // Weight evidence by strength const weights = { weak: 0.2, moderate: 0.5, strong: 1.0 }; const supportScore = supporting.reduce((sum, e) => sum + weights[e.strength], 0); const contradictScore = contradicting.reduce((sum, e) => sum + weights[e.strength], 0); if (supportScore + contradictScore === 0) return 0; return supportScore / (supportScore + contradictScore); } private generateFixSuggestion(hypothesis: string, evidence: Evidence[]): string { const hypothesisLower = hypothesis.toLowerCase(); if (hypothesisLower.includes('race condition')) { return 'Implement proper synchronization using locks, mutexes, or atomic operations'; } else if (hypothesisLower.includes('n+1') || hypothesisLower.includes('query')) { return 'Use eager loading with includes() or batch queries to reduce database calls'; } else if (hypothesisLower.includes('memory leak')) { return 'Ensure proper cleanup of timers, listeners, and large objects in cleanup/unmount methods'; } else if (hypothesisLower.includes('state corruption')) { return 'Use immutable updates and avoid direct state mutation'; } else if (hypothesisLower.includes('deadlock')) { return 'Acquire locks in consistent order across all code paths'; } // Generic suggestion based on evidence const mostCommonIssue = this.findMostCommonIssue(evidence); return `Address ${mostCommonIssue} issues found in the code`; } private findMostCommonIssue(evidence: Evidence[]): string { const issueCounts = new Map<string, number>(); for (const e of evidence) { const key = e.description.split(' ')[0]; issueCounts.set(key, (issueCounts.get(key) || 0) + 1); } let maxCount = 0; let mostCommon = 'identified'; for (const [issue, count] of issueCounts) { if (count > maxCount) { maxCount = count; mostCommon = issue; } } return mostCommon; } private findMatches( content: string, patterns: RegExp[], ): Array<{ match: string; line: number; index: number }> { const matches: Array<{ match: string; line: number; index: number }> = []; for (const pattern of patterns) { const patternMatches = [...content.matchAll(pattern)]; for (const match of patternMatches) { matches.push({ match: match[0], line: this.getLineNumber(content, match.index || 0), index: match.index || 0, }); } } return matches; } private isLoopStart(line: string): boolean { const loopKeywords = ['for', 'while', 'forEach', 'map', 'filter', 'reduce']; return loopKeywords.some(keyword => line.includes(keyword) && (line.includes('(') || line.includes('{')), ); } private getLineNumber(content: string, position: number): number { const lines = content.substring(0, position).split('\n'); return lines.length; } private async readFile(filePath: string): Promise<string> { try { return await fs.readFile(filePath, 'utf-8'); } catch (error) { throw new Error(`Cannot read file ${filePath}: ${error}`); } } }

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/haasonsaas/deep-code-reasoning-mcp'

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