Skip to main content
Glama

@missionsquad/mcp-rss

Official
cache.ts4 kB
import { CacheEntry, FeedResult } from '../types.js'; import { config } from '../config.js'; import { logger } from '../logger.js'; export class FeedCache { private cache: Map<string, CacheEntry> = new Map(); private accessOrder: string[] = []; private cleanupInterval?: NodeJS.Timeout; constructor() { // Start cleanup interval this.cleanupInterval = setInterval(() => { this.cleanup(); }, config.rssCacheCleanupInterval); } /** * Get a cached feed if available and not expired */ get(url: string): FeedResult | null { const entry = this.cache.get(url); if (!entry) return null; if (Date.now() > entry.expiresAt) { this.cache.delete(url); this.removeFromAccessOrder(url); return null; } // Update access order (LRU) this.updateAccessOrder(url); return entry.data; } /** * Get cache metadata without the full data */ getMetadata(url: string): { etag?: string; lastModified?: string } | null { const entry = this.cache.get(url); if (!entry) return null; return { etag: entry.etag, lastModified: entry.lastModified, }; } /** * Store a feed in cache */ set(url: string, data: FeedResult, ttl?: number): void { const expiresAt = Date.now() + (ttl || config.rssCacheTTL); this.cache.set(url, { data, expiresAt, etag: data.etag, lastModified: data.lastModified, }); this.updateAccessOrder(url); // Enforce max cache size if (this.cache.size > config.rssCacheMaxSize) { this.evictLRU(); } logger.debug(`Cached feed: ${url}, expires at: ${new Date(expiresAt).toISOString()}`); } /** * Update existing cache entry with new data */ update(url: string, data: FeedResult): void { const existing = this.cache.get(url); if (existing) { existing.data = data; existing.etag = data.etag; existing.lastModified = data.lastModified; logger.debug(`Updated cached feed: ${url}`); } } /** * Check if a feed is cached (regardless of expiration) */ has(url: string): boolean { return this.cache.has(url); } /** * Remove a feed from cache */ delete(url: string): void { this.cache.delete(url); this.removeFromAccessOrder(url); } /** * Clear all cached feeds */ clear(): void { this.cache.clear(); this.accessOrder = []; logger.info('Feed cache cleared'); } /** * Get cache statistics */ getStats(): { size: number; urls: string[] } { return { size: this.cache.size, urls: Array.from(this.cache.keys()), }; } /** * Clean up expired entries */ private cleanup(): void { const now = Date.now(); let removed = 0; for (const [url, entry] of this.cache.entries()) { if (now > entry.expiresAt) { this.cache.delete(url); this.removeFromAccessOrder(url); removed++; } } if (removed > 0) { logger.debug(`Cleaned up ${removed} expired cache entries`); } } /** * Update LRU access order */ private updateAccessOrder(url: string): void { this.removeFromAccessOrder(url); this.accessOrder.push(url); } /** * Remove from access order array */ private removeFromAccessOrder(url: string): void { const index = this.accessOrder.indexOf(url); if (index > -1) { this.accessOrder.splice(index, 1); } } /** * Evict least recently used entry */ private evictLRU(): void { if (this.accessOrder.length > 0) { const url = this.accessOrder[0]; this.delete(url); logger.debug(`Evicted LRU cache entry: ${url}`); } } /** * Destroy cache and clear intervals */ destroy(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } this.clear(); } } // Singleton instance export const feedCache = new FeedCache();

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/MissionSquad/mcp-rss'

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