Skip to main content
Glama
haasonsaas

Deep Code Reasoning MCP Server

by haasonsaas
ExecutionTracer.ts13.4 kB
import * as fs from 'fs/promises'; import type { CodeLocation, ExecutionStep, ExecutionPath, StateChange, ComplexityMetrics, } from '../models/types.js'; interface ExecutionNode { id: string; location: CodeLocation; type: 'function' | 'method' | 'conditional' | 'loop' | 'assignment'; data: ASTNode; children: ExecutionNode[]; } interface ASTNode { type: string; name?: string; location?: ASTLocation; body?: ASTNode[]; declarations?: ASTNode[]; methods?: ASTNode[]; stateChanges?: StateChange[]; callee?: ASTNode; property?: ASTNode; left?: ASTNode; right?: ASTNode; id?: ASTNode; init?: ASTNode; object?: ASTNode; } interface ASTLocation { start: { line: number; column?: number }; end: { line: number; column?: number }; } interface ExecutionGraph { nodes: Map<string, ExecutionNode>; edges: Array<{ from: string; to: string; condition?: string }>; entryPoint: string; } interface ParsedAST { functions: ASTNode[]; } export class ExecutionTracer { private cache: Map<string, ParsedAST>; constructor() { this.cache = new Map(); } async traceSemanticFlow( entryPoint: CodeLocation, maxDepth: number = 10, includeDataFlow: boolean = true, ): Promise<ExecutionGraph> { const graph: ExecutionGraph = { nodes: new Map(), edges: [], entryPoint: this.locationToId(entryPoint), }; // Start tracing from entry point await this.traceFromLocation(entryPoint, graph, 0, maxDepth, includeDataFlow); return graph; } private async traceFromLocation( location: CodeLocation, graph: ExecutionGraph, currentDepth: number, maxDepth: number, includeDataFlow: boolean, ): Promise<void> { if (currentDepth >= maxDepth) return; const nodeId = this.locationToId(location); if (graph.nodes.has(nodeId)) return; // Already processed try { // Read and parse the file const fileContent = await this.readFile(location.file); const ast = await this.parseFile(location.file, fileContent); // Find the function/method at the given location const targetNode = this.findNodeAtLocation(ast, location); if (!targetNode) return; // Create execution node const execNode: ExecutionNode = { id: nodeId, location, type: this.getNodeType(targetNode), data: targetNode, children: [], }; graph.nodes.set(nodeId, execNode); // Analyze the function body if (includeDataFlow) { await this.analyzeDataFlow(targetNode, execNode, graph); } // Find all function calls within this function const functionCalls = this.findFunctionCalls(targetNode); for (const call of functionCalls) { const calledLocation = await this.resolveCallLocation(call, location.file); if (calledLocation) { // Add edge graph.edges.push({ from: nodeId, to: this.locationToId(calledLocation), condition: call.condition, }); // Recursively trace await this.traceFromLocation( calledLocation, graph, currentDepth + 1, maxDepth, includeDataFlow, ); } } } catch (error) { console.error(`Error tracing location ${location.file}:${location.line}:`, error); } } 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}`); } } private async parseFile(filePath: string, content: string): Promise<ParsedAST> { const cacheKey = `${filePath}:${content.length}`; const cached = this.cache.get(cacheKey); if (cached) { return cached; } // Simple AST representation for our purposes const parsed = this.parseTypeScriptContent(content); this.cache.set(cacheKey, parsed); return parsed; } private parseTypeScriptContent(content: string): ParsedAST { // Simplified parsing - in production, use proper TypeScript compiler API const lines = content.split('\n'); const functions: ASTNode[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const functionMatch = line.match(/(?:async\s+)?(?:function\s+)?(\w+)\s*\([^)]*\)\s*{?/); if (functionMatch) { functions.push({ name: functionMatch[1], location: { start: { line: i + 1 }, end: { line: i + 1 } }, type: 'FunctionDeclaration', } as ASTNode); } } return { functions }; } private findNodeAtLocation(ast: ParsedAST, location: CodeLocation): ASTNode | null { // Simplified AST traversal to find node at specific location // In a real implementation, this would use proper AST visitor pattern return this.traverseAST(ast, (node: ASTNode) => { if (node.location && node.location.start.line <= location.line && node.location.end.line >= location.line) { return node; } return null; }); } private traverseAST(node: ASTNode | ParsedAST, visitor: (node: ASTNode) => ASTNode | null): ASTNode | null { // If it's a ParsedAST, traverse its functions if ('functions' in node) { for (const func of node.functions) { const result = this.traverseAST(func, visitor); if (result) return result; } return null; } const result = visitor(node); if (result) return result; const astNode = node as ASTNode; if (astNode.declarations) { for (const decl of astNode.declarations) { const result = this.traverseAST(decl, visitor); if (result) return result; } } if (astNode.methods) { for (const method of astNode.methods) { const result = this.traverseAST(method, visitor); if (result) return result; } } if (astNode.body && Array.isArray(astNode.body)) { for (const child of astNode.body) { const result = this.traverseAST(child, visitor); if (result) return result; } } return null; } private getNodeType(node: ASTNode): ExecutionNode['type'] { if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') { return 'function'; } if (node.type === 'MethodDeclaration') { return 'method'; } if (node.type === 'IfStatement' || node.type === 'ConditionalExpression') { return 'conditional'; } if (node.type === 'ForStatement' || node.type === 'WhileStatement' || node.type === 'DoWhileStatement') { return 'loop'; } return 'assignment'; } private async analyzeDataFlow( node: ASTNode, execNode: ExecutionNode, _graph: ExecutionGraph, ): Promise<void> { // Analyze variable assignments and state changes const assignments = this.findAssignments(node); const stateChanges: StateChange[] = []; for (const assignment of assignments) { stateChanges.push({ variable: assignment.variable, oldValue: null, // Would need more sophisticated analysis newValue: assignment.value, scope: this.determineScope(assignment), }); } execNode.data.stateChanges = stateChanges; } private findFunctionCalls(node: ASTNode): Array<{ name: string; condition?: string }> { const calls: Array<{ name: string; condition?: string }> = []; this.traverseAST(node, (child: ASTNode) => { if (child.type === 'CallExpression') { calls.push({ name: this.extractCallName(child), condition: this.extractCondition(child), }); } return null; }); return calls; } private extractCallName(callNode: ASTNode): string { if (callNode.callee?.type === 'Identifier') { return callNode.callee.name || 'unknown'; } if (callNode.callee?.type === 'MemberExpression') { return `${this.extractCallName(callNode.callee.object || {} as ASTNode)}.${callNode.callee.property?.name || 'unknown'}`; } return 'unknown'; } private extractCondition(_node: ASTNode): string | undefined { // Check if this call is within a conditional // Simplified implementation return undefined; } private async resolveCallLocation( call: { name: string; condition?: string }, currentFile: string, ): Promise<CodeLocation | null> { // Resolve function call to its definition // This would need proper symbol resolution // For now, return a simplified version // Check if it's a method call if (call.name.includes('.')) { const [_objectName, _methodName] = call.name.split('.'); // Would need to resolve object type and find method } // Check current file const fileContent = await this.readFile(currentFile); const lines = fileContent.split('\n'); for (let i = 0; i < lines.length; i++) { if (lines[i].includes(`function ${call.name}`) || lines[i].includes(`${call.name}(`) || lines[i].includes(`${call.name} =`)) { return { file: currentFile, line: i + 1, functionName: call.name, }; } } return null; } private findAssignments(node: ASTNode): Array<{ variable: string; value: ASTNode | undefined; location: ASTLocation | undefined }> { const assignments: Array<{ variable: string; value: ASTNode | undefined; location: ASTLocation | undefined }> = []; this.traverseAST(node, (child: ASTNode) => { if (child.type === 'AssignmentExpression' || child.type === 'VariableDeclarator') { assignments.push({ variable: this.extractVariableName(child.left || child.id), value: child.right || child.init, location: child.location, }); } return null; }); return assignments; } private extractVariableName(node: ASTNode | undefined): string { if (!node) return 'unknown'; if (node.type === 'Identifier') { return node.name || 'unknown'; } if (node.type === 'MemberExpression') { return `${this.extractVariableName(node.object)}.${node.property?.name || 'unknown'}`; } return 'unknown'; } private determineScope(assignment: { variable: string; value: ASTNode | undefined; location: ASTLocation | undefined }): 'local' | 'global' | 'instance' { // Simplified scope determination if (assignment.variable.startsWith('this.')) { return 'instance'; } // Would need proper scope analysis return 'local'; } private locationToId(location: CodeLocation): string { return `${location.file}:${location.line}:${location.column || 0}`; } async buildExecutionPath(graph: ExecutionGraph, pathId: string): Promise<ExecutionPath> { const steps: ExecutionStep[] = []; const visited = new Set<string>(); // Build path from entry point await this.buildPathSteps(graph, graph.entryPoint, steps, visited); // Calculate complexity metrics const complexity = this.calculateComplexity(steps); return { id: pathId, steps, totalDuration: steps.reduce((sum, step) => sum + (step.duration || 0), 0), complexity, }; } private async buildPathSteps( graph: ExecutionGraph, nodeId: string, steps: ExecutionStep[], visited: Set<string>, ): Promise<void> { if (visited.has(nodeId)) return; visited.add(nodeId); const node = graph.nodes.get(nodeId); if (!node) return; const step: ExecutionStep = { location: node.location, operation: node.type, inputs: [], // Would need data flow analysis outputs: [], // Would need data flow analysis stateChanges: node.data.stateChanges || [], duration: this.estimateDuration(node), }; steps.push(step); // Follow edges const outgoingEdges = graph.edges.filter(e => e.from === nodeId); for (const edge of outgoingEdges) { await this.buildPathSteps(graph, edge.to, steps, visited); } } private estimateDuration(node: ExecutionNode): number { // Simplified duration estimation switch (node.type) { case 'loop': return 10; // Base loop overhead case 'conditional': return 1; case 'function': case 'method': return 5; default: return 0.1; } } private calculateComplexity(steps: ExecutionStep[]): ComplexityMetrics { let cyclomaticComplexity = 1; // Base complexity let cognitiveComplexity = 0; let nestedDepth = 0; let maxNestedDepth = 0; for (const step of steps) { if (step.operation === 'conditional') { cyclomaticComplexity++; cognitiveComplexity += (1 + nestedDepth); } else if (step.operation === 'loop') { cyclomaticComplexity++; cognitiveComplexity += (1 + nestedDepth); nestedDepth++; maxNestedDepth = Math.max(maxNestedDepth, nestedDepth); } } // Estimate Big O complexity const hasNestedLoops = maxNestedDepth >= 2; const bigOTime = hasNestedLoops ? `O(n^${maxNestedDepth})` : 'O(n)'; const bigOSpace = 'O(1)'; // Simplified return { cyclomaticComplexity, cognitiveComplexity, bigOTime, bigOSpace, }; } }

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