Skip to main content
Glama

MCP-REPL

by AnEntrypoint
MIT License
3,295
104
  • Linux
  • Apple
cross-tool-context.js17.6 kB
// Enhanced cross-tool communication with time caps and smart caching import { v4 as uuidv4 } from 'uuid'; // Smart cache with TTL and memory management class SmartCache { constructor(maxSize = 1000, defaultTTL = 300000) { // 5 minutes default this.cache = new Map(); this.maxSize = maxSize; this.defaultTTL = defaultTTL; this.accessTimes = new Map(); } set(key, value, ttl = this.defaultTTL) { // Clean up if cache is full if (this.cache.size >= this.maxSize) { this.cleanup(); } const expires = Date.now() + ttl; this.cache.set(key, { value, expires }); this.accessTimes.set(key, Date.now()); } get(key) { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() > entry.expires) { this.cache.delete(key); this.accessTimes.delete(key); return null; } this.accessTimes.set(key, Date.now()); return entry.value; } has(key) { return this.get(key) !== null; } delete(key) { this.cache.delete(key); this.accessTimes.delete(key); } clear() { this.cache.clear(); this.accessTimes.clear(); } cleanup() { const now = Date.now(); const toDelete = []; // Remove expired entries for (const [key, entry] of this.cache) { if (now > entry.expires) { toDelete.push(key); } } // Remove least recently used if still over size if (this.cache.size - toDelete.length >= this.maxSize) { const sorted = Array.from(this.accessTimes.entries()) .sort(([, a], [, b]) => a - b); const toRemove = sorted.slice(0, this.cache.size - this.maxSize + toDelete.length); toDelete.push(...toRemove.map(([key]) => key)); } toDelete.forEach(key => this.delete(key)); } getStats() { return { size: this.cache.size, maxSize: this.maxSize, hitRate: this.hitRate || 0 }; } } // Delta-based context manager class DeltaContextManager { constructor() { this.baseContext = new Map(); this.deltas = new Map(); this.lastSync = Date.now(); } updateBaseContext(key, value) { const oldValue = this.baseContext.get(key); this.baseContext.set(key, value); // Store delta this.deltas.set(`${key}_${Date.now()}`, { key, oldValue, newValue: value, timestamp: Date.now() }); // Clean old deltas (keep only last 100) if (this.deltas.size > 100) { const sorted = Array.from(this.deltas.entries()) .sort(([, a], [, b]) => a.timestamp - b.timestamp); const toDelete = sorted.slice(0, this.deltas.size - 100); toDelete.forEach(([key]) => this.deltas.delete(key)); } } getDeltas(since) { const relevantDeltas = []; for (const [id, delta] of this.deltas) { if (delta.timestamp > since) { relevantDeltas.push(delta); } } return relevantDeltas; } getContext(since = null) { if (!since) { return Object.fromEntries(this.baseContext); } const deltas = this.getDeltas(since); const context = {}; // Apply deltas in order deltas.forEach(delta => { context[delta.key] = delta.newValue; }); return context; } compress() { // Clean old deltas (older than 5 minutes) const cutoff = Date.now() - 300000; for (const [id, delta] of this.deltas) { if (delta.timestamp < cutoff) { this.deltas.delete(id); } } } } export class CrossToolContext { constructor() { this.toolSessions = new Map(); this.currentSession = null; this.globalContext = new DeltaContextManager(); this.smartCache = new SmartCache(1000, 300000); // 5 minute TTL this.resultCache = new SmartCache(500, 180000); // 3 minute TTL for results this.duplicationSuppression = new Map(); this.reportedItems = new Map(); // Track reported items for suppression this.lastCleanup = Date.now(); } // Time cap constants static TIME_CAPS = { ANALYSIS: 60000, // 1 minute for analysis LINT: 30000, // 30 seconds for linting EXECUTION: 120000, // 2 minutes for execution SEARCH: 45000, // 45 seconds for search DEFAULT: 30000 // 30 seconds default }; // Get time cap for tool type getTimeCap(toolName) { const name = toolName.toLowerCase(); if (name.includes('ast') || name.includes('analysis')) return this.constructor.TIME_CAPS.ANALYSIS; if (name.includes('lint')) return this.constructor.TIME_CAPS.LINT; if (name.includes('exec')) return this.constructor.TIME_CAPS.EXECUTION; if (name.includes('search')) return this.constructor.TIME_CAPS.SEARCH; return this.constructor.TIME_CAPS.DEFAULT; } // Check for duplicate operations isDuplicate(toolName, workingDirectory, query, args) { const signature = this.createOperationSignature(toolName, workingDirectory, query, args); const lastOperation = this.duplicationSuppression.get(signature); if (lastOperation && Date.now() - lastOperation < this.getTimeCap(toolName)) { return true; } this.duplicationSuppression.set(signature, Date.now()); // Clean old suppression entries this.cleanupSuppression(); return false; } createOperationSignature(toolName, workingDirectory, query, args) { const keyData = { toolName, workingDirectory, query: query ? query.substring(0, 100) : '', argsHash: this.hashArgs(args) }; return JSON.stringify(keyData); } hashArgs(args) { // Simple hash of relevant args const relevant = {}; for (const [key, value] of Object.entries(args)) { if (typeof value === 'string') { relevant[key] = value.substring(0, 50); } else if (typeof value === 'number' || typeof value === 'boolean') { relevant[key] = value; } } return JSON.stringify(relevant); } cleanupSuppression() { const now = Date.now(); if (now - this.lastCleanup > 60000) { // Clean every minute const toDelete = []; for (const [signature, timestamp] of this.duplicationSuppression) { if (now - timestamp > 300000) { // 5 minutes toDelete.push(signature); } } toDelete.forEach(signature => this.duplicationSuppression.delete(signature)); // Clean old reported items (older than 10 minutes) const reportedToDelete = []; for (const [signature, timestamp] of this.reportedItems) { if (now - timestamp > 600000) { // 10 minutes reportedToDelete.push(signature); } } reportedToDelete.forEach(signature => this.reportedItems.delete(signature)); this.lastCleanup = now; } } // Check if an item has been reported and should be suppressed isReported(category, identifier) { const signature = `${category}:${identifier}`; const lastReported = this.reportedItems.get(signature); if (lastReported && Date.now() - lastReported < 600000) { // 10 minutes return true; } this.reportedItems.set(signature, Date.now()); return false; } // Generate concise report for cross-tool responses generateConciseReport(toolName, workingDirectory, result) { const report = { tool: toolName, location: workingDirectory, timestamp: Date.now(), summary: this.createSummary(result) }; // Only include essential information if (result.filesAccessed && result.filesAccessed.length > 0) { report.files = result.filesAccessed.slice(0, 3); // Max 3 files } if (result.patterns && result.patterns.length > 0) { report.patterns = result.patterns.slice(0, 2); // Max 2 patterns } return report; } // Create a concise summary from result data createSummary(result) { if (result.error) { return `Error: ${result.error.message || result.error}`; } if (result.content && Array.isArray(result.content)) { const textContent = result.content.find(c => c.type === 'text')?.text || ''; if (textContent.startsWith('[')) { // JSON array try { const parsed = JSON.parse(textContent); if (Array.isArray(parsed)) { return `${parsed.length} items found`; } } catch { // Fall through to text summary } } return textContent.substring(0, 100) + (textContent.length > 100 ? '...' : ''); } if (result.stdout) { return result.stdout.split('\n')[0].substring(0, 100) + (result.stdout.length > 100 ? '...' : ''); } return 'Operation completed'; } // Suppress duplicate reporting with enhanced checks shouldReport(toolName, workingDirectory, result) { // Create signature for this report const signature = this.createReportSignature(toolName, workingDirectory, result); // Check if we've reported something very similar recently return !this.isReported('tool_report', signature); } createReportSignature(toolName, workingDirectory, result) { const keyParts = [ toolName, workingDirectory, result.error ? 'error' : 'success', result.content ? 'content' : 'no-content', result.filesAccessed?.length || 0, result.patterns?.length || 0 ]; return keyParts.join('|'); } // Smart caching for intermediate results cacheResult(toolName, workingDirectory, key, result, ttl = null) { const cacheKey = `${toolName}_${workingDirectory}_${key}`; const resultTTL = ttl || this.getTimeCap(toolName); this.resultCache.set(cacheKey, result, resultTTL); } getCachedResult(toolName, workingDirectory, key) { const cacheKey = `${toolName}_${workingDirectory}_${key}`; return this.resultCache.get(cacheKey); } // Cache computation results cacheComputation(toolName, key, computation, ttl = null) { if (this.smartCache.has(key)) { return this.smartCache.get(key); } const result = computation(); const cacheTTL = ttl || this.getTimeCap(toolName); this.smartCache.set(key, result, cacheTTL); return result; } // Start a new tool session with time cap startToolSession({ id = uuidv4(), toolName, workingDirectory, query, startTime = Date.now(), args = {} } = {}) { // Check for duplicates - only for expensive operations if (this.isDuplicate(toolName, workingDirectory, query, args) && ['searchcode', 'ast_tool'].includes(toolName)) { // Silently return cached result instead of throwing error const cachedResult = this.getCachedResult(toolName, workingDirectory, this.createOperationSignature(toolName, workingDirectory, query, args)); if (cachedResult) { return cachedResult; } } const session = { id, toolName, workingDirectory, query, startTime, timeCap: this.getTimeCap(toolName), status: 'running', result: null, error: null, relatedSessions: [], metadata: {} }; this.toolSessions.set(id, session); this.currentSession = id; return session; } // Complete current tool session completeCurrentSession(result) { if (!this.currentSession) return null; const session = this.toolSessions.get(this.currentSession); if (session) { session.status = 'completed'; session.endTime = Date.now(); session.duration = session.endTime - session.startTime; session.result = result; // Cache result if under time cap if (session.duration < session.timeCap) { this.cacheResult( session.toolName, session.workingDirectory, session.query || 'default', result, session.timeCap ); } // Update global context with delta this.updateGlobalContext(session); return session; } return null; } // Get current tool session with time cap check getCurrentSession() { if (!this.currentSession) return null; const session = this.toolSessions.get(this.currentSession); // Clear current if it's too old (exceeded time cap) if (session && Date.now() - session.startTime > session.timeCap) { this.currentSession = null; return null; } return session; } // Update global context using delta-based updates updateGlobalContext(session) { const key = `${session.toolName}_${session.workingDirectory}`; const currentContext = this.globalContext.getContext(); let context = currentContext[key] || { toolName: session.toolName, workingDirectory: session.workingDirectory, totalCalls: 0, successfulCalls: 0, failedCalls: 0, averageDuration: 0, lastUsed: 0, commonPatterns: new Set(), recentFiles: new Set() }; // Create delta context.totalCalls++; context.lastUsed = Date.now(); if (session.status === 'completed') { context.successfulCalls++; } else if (session.status === 'failed') { context.failedCalls++; } // Update average duration if (session.duration) { context.averageDuration = (context.averageDuration * (context.totalCalls - 1) + session.duration) / context.totalCalls; } // Store session-specific metadata if (session.metadata) { if (session.metadata.patterns) { session.metadata.patterns.forEach(pattern => context.commonPatterns.add(pattern)); } if (session.metadata.filesAccessed) { session.metadata.filesAccessed.forEach(file => context.recentFiles.add(file)); } } this.globalContext.updateBaseContext(key, context); } // Clean up old sessions and compress context cleanup() { const now = Date.now(); const toDelete = []; // Delete old sessions for (const [id, session] of this.toolSessions) { const age = now - session.startTime; if (age > 300000) { // 5 minutes toDelete.push(id); } } toDelete.forEach(id => { this.toolSessions.delete(id); if (this.currentSession === id) { this.currentSession = null; } }); // Compress context this.globalContext.compress(); // Clean caches this.smartCache.cleanup(); this.resultCache.cleanup(); } // Get cache statistics getCacheStats() { return { smartCache: this.smartCache.getStats(), resultCache: this.resultCache.getStats(), sessions: this.toolSessions.size, duplicatesSuppressed: this.duplicationSuppression.size }; } } // Global instance export const crossToolContext = new CrossToolContext(); // Enhanced middleware function with smart caching and duplication suppression export function withCrossToolAwareness(toolHandler, toolName) { return async (args) => { const workingDirectory = args.workingDirectory || process.cwd(); const query = args.query || args.pattern || args.code || ''; // Start tool session with time cap and duplication check const session = crossToolContext.startToolSession({ toolName, workingDirectory, query, args }); try { // Check cache first const cacheKey = JSON.stringify(args); const cachedResult = crossToolContext.getCachedResult(toolName, workingDirectory, cacheKey); if (cachedResult) { // Return cached result with metadata return { ...cachedResult, _cached: true, _cacheTimestamp: Date.now() }; } // Execute the tool with smart caching for expensive operations const result = await crossToolContext.cacheComputation( toolName, cacheKey, () => toolHandler(args) ); // Complete session and cache result crossToolContext.completeCurrentSession(result); // Add session metadata if (session) { session.metadata = { filesAccessed: result._filesAccessed || [], patterns: result._patterns || [], insights: result._insights || [] }; } return result; } catch (error) { // Fail session and return error gracefully crossToolContext.failCurrentSession(error); return { content: [{ type: "text", text: `Error in ${toolName}: ${error.message}${error.stack ? '\n' + error.stack : ''}` }], _isError: true }; } }; } // Utility function to add tool-specific metadata with concise reporting export function addToolMetadata(result, metadata) { const enhancedResult = { ...result, ...metadata }; // Add concise report if this should be reported if (metadata.toolName && metadata.workingDirectory) { if (crossToolContext.shouldReport(metadata.toolName, metadata.workingDirectory, result)) { const conciseReport = crossToolContext.generateConciseReport( metadata.toolName, metadata.workingDirectory, result ); enhancedResult._conciseReport = conciseReport; } } return enhancedResult; } // Utility function to generate location-based concise report export function generateLocationReport(toolName, workingDirectory, files = [], summary = '') { const report = { tool: toolName, location: workingDirectory, timestamp: Date.now(), files: files.slice(0, 5), // Max 5 files summary: summary || `${files.length} files processed` }; // Only report if not recently reported const signature = `${toolName}:${workingDirectory}:${files.length}:${summary.substring(0, 50)}`; if (!crossToolContext.isReported('location_report', signature)) { return report; } return null; // Suppressed }

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/AnEntrypoint/mcp-repl'

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