mcp-think-tank
Version:
Structured thinking and knowledge management tool for Model Context Protocol
132 lines (131 loc) • 4.11 kB
JavaScript
/**
* ContentCacheAdapter.ts
*
* Adapter for file/URL operations that enables content-based caching
* Part of Story 3-G: Execution cache for file/URL reads
*/
import fs from 'fs/promises';
import axios from 'axios';
import { toolManager } from './ToolManager.js';
/**
* Read a file with content-based caching
*
* @param filePath Path to the file to read
* @param options Options for fs.readFile
* @returns The file contents
*/
export async function readFile(filePath, options) {
return toolManager.readFileWithCache(filePath, options);
}
/**
* Read a JSON file with content-based caching
*
* @param filePath Path to the JSON file
* @returns Parsed JSON object
*/
export async function readJsonFile(filePath) {
const content = await toolManager.readFileWithCache(filePath, 'utf8');
if (typeof content !== 'string') {
throw new Error('Expected string content when reading JSON file');
}
return JSON.parse(content);
}
/**
* Fetch content from a URL with content-based caching
*
* @param url URL to fetch
* @returns Response content as string
*/
export async function fetchUrl(url) {
return toolManager.fetchUrlWithCache(url);
}
/**
* Fetch JSON from a URL with content-based caching
*
* @param url URL to fetch JSON from
* @param config Optional axios config
* @returns Parsed JSON object
*/
export async function fetchJson(url, config) {
try {
// For JSON APIs, we should use axios to properly handle headers and request config
// We'll use a workaround to integrate with our caching system
// First check if the URL is already cached
const cachedContent = await toolManager.fetchUrlWithCache(url)
.catch(() => null); // Ignore cache errors and proceed to fresh fetch
if (cachedContent) {
try {
// Try to parse the cached content as JSON
return JSON.parse(cachedContent);
}
catch (e) {
// If parsing fails, proceed with fresh fetch
console.log(`[ContentCacheAdapter] Cached content not valid JSON, fetching fresh`);
}
}
// Perform a fresh fetch with axios
const response = await axios.get(url, config);
// The actual response will be cached by fetchUrlWithCache on subsequent calls
return response.data;
}
catch (error) {
console.error(`[ContentCacheAdapter] Error fetching JSON: ${error}`);
throw error;
}
}
/**
* List files in a directory with caching based on directory stats
*
* @param dirPath Directory path to list
* @returns Array of file names
*/
export async function listDirectory(dirPath) {
try {
// Get directory stats to check for changes
const stats = await fs.stat(dirPath);
const dirKey = `dir:${dirPath}:${stats.mtime.getTime()}`;
// Check cache first
const cachedEntries = toolManager.getContentCacheItem(dirKey);
if (cachedEntries) {
console.log(`[ContentCacheAdapter] Cache hit for directory listing ${dirPath}`);
return cachedEntries;
}
// Read directory entries
const entries = await fs.readdir(dirPath);
// Cache the result
toolManager.setContentCacheItem(dirKey, entries);
return entries;
}
catch (error) {
console.error(`[ContentCacheAdapter] Error listing directory: ${error}`);
// Fall back to regular readdir
return fs.readdir(dirPath);
}
}
/**
* Check if the content cache is accessible and working
*
* @returns True if the content cache is available
*/
export function isContentCacheAvailable() {
try {
const stats = toolManager.getContentCacheStats();
return stats.enabled;
}
catch (e) {
return false;
}
}
/**
* Get stats about the content cache
*
* @returns Object with cache stats
*/
export function getContentCacheStats() {
try {
return toolManager.getContentCacheStats();
}
catch (e) {
return { available: false, error: e.message };
}
}