Skip to main content
Glama
FosterG4

Code Reference Optimizer MCP Server

by FosterG4
CodeReferenceOptimizer.ts9.98 kB
import type { ASTParser } from '../parsers/ASTParser.js'; import type { CacheManager } from '../cache/CacheManager.js'; import type { DiffManager } from '../diff/DiffManager.js'; import type { ImportAnalyzer } from '../analysis/ImportAnalyzer.js'; import type { CodeContext, ExtractContextOptions, DiffAnalysisOptions, ImportOptimizationOptions } from '../types/index.js'; export class CodeReferenceOptimizer { constructor( private astParser: ASTParser, private cacheManager: CacheManager, private diffManager: DiffManager, private importAnalyzer: ImportAnalyzer ) {} /** * Extract minimal code context using AST parsing and caching */ async extractCodeContext(options: ExtractContextOptions): Promise<CodeContext> { const { filePath, targetSymbols, includeImports, maxTokens } = options; // Check cache first const cacheKey = this.generateCacheKey(filePath, targetSymbols, includeImports); const cached = await this.cacheManager.get(cacheKey); if (cached && !this.isStale(cached, filePath)) { return this.truncateToTokenLimit(cached, maxTokens || 1000); } // Parse file and extract context const ast = await this.astParser.parseFile(filePath); const extractedContext = await this.astParser.extractContext(ast, targetSymbols); // Optimize imports if requested let optimizedImports: string[] = []; if (includeImports) { const usedSymbols = this.extractUsedSymbols(extractedContext); optimizedImports = await this.importAnalyzer.getMinimalImports(filePath, usedSymbols); } const context: CodeContext = { filePath, extractedCode: extractedContext.code, imports: optimizedImports, symbols: extractedContext.symbols, dependencies: extractedContext.dependencies, tokenCount: this.estimateTokenCount(extractedContext.code + optimizedImports.join('\n')), timestamp: Date.now(), relevanceScore: this.calculateRelevanceScore(extractedContext, targetSymbols), }; // Cache the result await this.cacheManager.set(cacheKey, context); return this.truncateToTokenLimit(context, maxTokens || 1000); } /** * Get cached context for a file */ async getCachedContext(filePath: string, cacheKey?: string): Promise<CodeContext | null> { if (cacheKey) { return await this.cacheManager.get(cacheKey); } // Find most relevant cached context for the file const allCached = await this.cacheManager.getByFilePath(filePath); if (allCached.length === 0) { return null; } // Return the most recent and relevant cached context const sortedCached = (allCached || []).sort((a, b) => { const scoreA = a.relevanceScore * (1 - this.getAgeWeight(a.timestamp)); const scoreB = b.relevanceScore * (1 - this.getAgeWeight(b.timestamp)); return scoreB - scoreA; }); return sortedCached.length > 0 ? (sortedCached[0] as CodeContext) : null; } /** * Analyze code differences and provide minimal updates */ async analyzeCodeDiff(options: DiffAnalysisOptions): Promise<{ changes: Array<{ type: 'added' | 'removed' | 'modified'; symbol: string; code: string; lineNumber: number; }>; affectedSymbols: string[]; minimalUpdate: string; tokenSavings: number; }> { const { filePath, oldContent, newContent } = options; // Create snapshot from old content this.diffManager.createSnapshotFromContent(filePath, oldContent); // Extract symbols from old content const oldSymbols = await this.extractSymbolsFromContent(oldContent, filePath); await this.diffManager.createSymbolSnapshots(filePath, oldSymbols); // Extract symbols from new content const newSymbols = await this.extractSymbolsFromContent(newContent, filePath); // Generate symbol-level diff const symbolChanges = await this.diffManager.generateSymbolDiff(filePath, newSymbols); // Convert SymbolChange[] to the expected format const changes = symbolChanges.map(change => ({ type: change.type, symbol: change.symbol, code: change.code, lineNumber: change.lineNumber, })); const affectedSymbols = changes.map(change => change.symbol); // Generate minimal update containing only changed parts const minimalUpdate = this.generateMinimalUpdate(changes); // Calculate token savings const fullContentTokens = this.estimateTokenCount(newContent); const minimalUpdateTokens = this.estimateTokenCount(minimalUpdate); const tokenSavings = fullContentTokens - minimalUpdateTokens; return { changes, affectedSymbols, minimalUpdate, tokenSavings, }; } /** * Optimize imports to reduce redundancy */ async optimizeImports(options: ImportOptimizationOptions): Promise<{ optimizedImports: string[]; removedImports: string[]; tokenSavings: number; }> { const { filePath, usedSymbols } = options; const originalImports = await this.importAnalyzer.extractImports(filePath); const optimizedImports = await this.importAnalyzer.getMinimalImports(filePath, usedSymbols || []); const removedImports = originalImports.filter( imp => !optimizedImports.includes(imp) ); const originalTokens = this.estimateTokenCount(originalImports.join('\n')); const optimizedTokens = this.estimateTokenCount(optimizedImports.join('\n')); const tokenSavings = originalTokens - optimizedTokens; return { optimizedImports, removedImports, tokenSavings, }; } private generateCacheKey(filePath: string, targetSymbols?: string[], includeImports?: boolean): string { const symbolsKey = targetSymbols ? targetSymbols.sort().join(',') : 'all'; const importsKey = includeImports ? 'with-imports' : 'no-imports'; return `${filePath}:${symbolsKey}:${importsKey}`; } private isStale(context: CodeContext, _filePath: string): boolean { // Check if file has been modified since cache entry // This would typically check file modification time const maxAge = 5 * 60 * 1000; // 5 minutes return Date.now() - context.timestamp > maxAge; } private truncateToTokenLimit(context: CodeContext, maxTokens: number): CodeContext { if (context.tokenCount <= maxTokens) { return context; } // Truncate code while preserving structure const lines = context.extractedCode.split('\n'); const truncatedLines: string[] = []; let currentTokens = this.estimateTokenCount(context.imports.join('\n')); for (const line of lines) { const lineTokens = this.estimateTokenCount(line); if (currentTokens + lineTokens > maxTokens) { break; } truncatedLines.push(line); currentTokens += lineTokens; } return { ...context, extractedCode: truncatedLines.join('\n'), tokenCount: currentTokens, }; } private extractUsedSymbols(context: { code: string; symbols: string[] }): string[] { // Extract symbols that are actually referenced in the code const usedSymbols: string[] = []; const codeText = context.code; for (const symbol of context.symbols) { if (codeText.includes(symbol)) { usedSymbols.push(symbol); } } return usedSymbols; } private calculateRelevanceScore(context: { symbols: string[] }, targetSymbols?: string[]): number { if (!targetSymbols || targetSymbols.length === 0) { return 1.0; } const matchingSymbols = context.symbols.filter(symbol => targetSymbols.includes(symbol) ); return matchingSymbols.length / targetSymbols.length; } private getAgeWeight(timestamp: number): number { const ageMs = Date.now() - timestamp; const maxAge = 24 * 60 * 60 * 1000; // 24 hours return Math.min(ageMs / maxAge, 1.0); } private generateMinimalUpdate(changes: Array<{ type: 'added' | 'removed' | 'modified'; symbol: string; code: string; lineNumber: number; }>): string { return changes .map(change => { const action = change.type === 'removed' ? 'REMOVE' : 'UPDATE'; return `// ${action} ${change.symbol} at line ${change.lineNumber}\n${change.code}`; }) .join('\n\n'); } private async extractSymbolsFromContent(content: string, filePath: string): Promise<Map<string, string>> { const symbols = new Map<string, string>(); try { const parsed = await this.astParser.parseContent(content, filePath); const extractedContext = await this.astParser.extractContext(parsed, []); // Extract each symbol with its code for (const symbol of extractedContext.symbols) { // For now, we'll use a simple approach to extract symbol code // In a more sophisticated implementation, we'd extract the actual symbol definition const symbolCode = this.extractSymbolCode(content, symbol); if (symbolCode) { symbols.set(symbol, symbolCode); } } } catch (error) { console.warn(`Failed to extract symbols from ${filePath}:`, error); } return symbols; } private extractSymbolCode(content: string, symbolName: string): string { const lines = content.split('\n'); const symbolRegex = new RegExp(`(class|function|interface|type|const|let|var)\\s+${symbolName}\\b`); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line && symbolRegex.test(line)) { // Simple extraction - just return the line for now // In a more sophisticated implementation, we'd extract the full symbol definition return line.trim(); } } return ''; } private estimateTokenCount(text: string): number { // Simple token estimation (roughly 4 characters per token) return Math.ceil(text.length / 4); } }

Implementation Reference

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/FosterG4/mcpsaver'

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