Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
version-aware-validator.ts4.69 kB
/** * Version-Aware Cache Validator * Detects MCP version changes and triggers per-MCP cache invalidation */ import { logger } from '../utils/logger.js'; import { CachePatcher, ToolMetadataCache } from './cache-patcher.js'; export interface MCPVersionChange { mcpName: string; cachedVersion: string; currentVersion: string; toolCount: number; requiresRefresh: boolean; } export class VersionAwareValidator { constructor(private cachePatcher: CachePatcher) {} /** * Validate cache against current MCP versions and detect changes * Returns list of MCPs that need cache refresh * * Handles backwards compatibility: * - Skips MCPs with version='unknown' (old cache files) * - Only compares when both versions are known */ async validateAndDetectVersionChanges( cache: ToolMetadataCache, currentMCPVersions: Record<string, string> ): Promise<MCPVersionChange[]> { const changes: MCPVersionChange[] = []; for (const [mcpName, mcpData] of Object.entries(cache.mcps)) { const cachedVersion = mcpData.serverInfo?.version || 'unknown'; const currentVersion = currentMCPVersions[mcpName]; // Skip if MCP no longer exists in profile if (!currentVersion) { logger.debug(`MCP ${mcpName} removed from profile`); continue; } // Skip version check if cached version is unknown (backwards compatibility) // This handles cache files created before version tracking was added if (cachedVersion === 'unknown') { logger.debug(`Skipping version check for ${mcpName} - cache from older NCP version`); continue; } const versionChanged = cachedVersion !== currentVersion; if (versionChanged) { logger.info( `📦 Version change detected for ${mcpName}: ${cachedVersion} → ${currentVersion}` ); changes.push({ mcpName, cachedVersion, currentVersion, toolCount: mcpData.tools?.length || 0, requiresRefresh: true }); } else { logger.debug(`✅ ${mcpName} version unchanged (${cachedVersion})`); } } return changes; } /** * Invalidate cache for specific MCPs that have version changes * This allows selective cache refresh without invalidating entire profile */ async invalidateMCPsInCache(mcpNames: string[]): Promise<void> { const cache = await this.cachePatcher.loadToolMetadataCache(); let invalidatedCount = 0; for (const mcpName of mcpNames) { if (cache.mcps[mcpName]) { const toolCount = cache.mcps[mcpName].tools?.length || 0; delete cache.mcps[mcpName]; logger.info(`🗑️ Invalidated cache for ${mcpName} (${toolCount} tools)`); invalidatedCount++; } } if (invalidatedCount > 0) { // Don't clear entire profile hash - just update timestamp cache.lastModified = Date.now(); await this.cachePatcher.saveToolMetadataCache(cache); } } /** * Invalidate embeddings cache for specific MCPs */ async invalidateEmbeddingsForMCPs(mcpNames: string[]): Promise<void> { for (const mcpName of mcpNames) { await this.cachePatcher.patchRemoveEmbeddings(mcpName); } } /** * Get a summary of version changes */ getSummary(changes: MCPVersionChange[]): string { if (changes.length === 0) { return 'All MCP versions are current'; } const lines = [ `Found ${changes.length} MCP version change(s) requiring cache refresh:`, ...changes.map( c => ` • ${c.mcpName}: ${c.cachedVersion} → ${c.currentVersion} (${c.toolCount} tools)` ) ]; return lines.join('\n'); } /** * Apply version changes by invalidating affected MCPs (atomic operation) * Ensures both metadata and embeddings are invalidated together */ async applyVersionChanges(changes: MCPVersionChange[]): Promise<void> { const mcpsToInvalidate = changes .filter(c => c.requiresRefresh) .map(c => c.mcpName); if (mcpsToInvalidate.length === 0) { return; } logger.info(`Refreshing cache for ${mcpsToInvalidate.length} MCP(s)...`); try { // Invalidate both metadata and embeddings together (atomic) // If either fails, the whole operation is considered failed await Promise.all([ this.invalidateMCPsInCache(mcpsToInvalidate), this.invalidateEmbeddingsForMCPs(mcpsToInvalidate) ]); logger.info(`✅ Cache refresh complete for: ${mcpsToInvalidate.join(', ')}`); } catch (error) { logger.error(`Failed to apply version changes: ${error}`); throw 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/portel-dev/ncp'

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