Skip to main content
Glama
bsim0927
by bsim0927
manager.js7.11 kB
/** * File storage and management for MCP server */ import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export class StorageManager { constructor(dataDir = null) { this.dataDir = dataDir || path.join(process.cwd(), 'data'); this.commandsDir = path.join(this.dataDir, 'commands'); this.outputsDir = path.join(this.dataDir, 'outputs'); this.tempDir = path.join(this.dataDir, 'temp'); } /** * Initialize storage directories */ async initialize() { try { await fs.mkdir(this.commandsDir, { recursive: true }); await fs.mkdir(this.outputsDir, { recursive: true }); await fs.mkdir(this.tempDir, { recursive: true }); } catch (error) { throw new Error(`Failed to initialize storage directories: ${error.message}`); } } /** * Generate a unique filename with timestamp and random string * @param {string} prefix - Filename prefix * @param {string} extension - File extension (without dot) * @returns {string} */ generateFilename(prefix, extension) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const random = Math.random().toString(36).substring(2, 8); return `${prefix}_${timestamp}_${random}.${extension}`; } /** * Get full path for a command file * @param {string} filename - Optional specific filename * @returns {string} */ getCommandPath(filename) { return path.join(this.commandsDir, filename || this.generateFilename('cmd', 'txt')); } /** * Get full path for an output file * @param {string} filename - Optional specific filename * @returns {string} */ getOutputPath(filename) { return path.join(this.outputsDir, filename || this.generateFilename('result', 'json')); } /** * Get full path for a temporary file * @param {string} filename - Optional specific filename * @returns {string} */ getTempPath(filename) { return path.join(this.tempDir, filename || this.generateFilename('temp', 'tmp')); } /** * Save command file * @param {string} content - Command file content * @param {string} filename - Optional specific filename * @returns {Promise<string>} Path to saved file */ async saveCommand(content, filename) { const filepath = this.getCommandPath(filename); try { await fs.writeFile(filepath, content, 'utf8'); return filepath; } catch (error) { throw new Error(`Failed to save command file: ${error.message}`); } } /** * Read command file * @param {string} filepath - Path to command file * @returns {Promise<string>} */ async readCommand(filepath) { try { return await fs.readFile(filepath, 'utf8'); } catch (error) { throw new Error(`Failed to read command file: ${error.message}`); } } /** * Check if output file exists and has content * @param {string} filepath - Path to output file * @returns {Promise<boolean>} */ async outputExists(filepath) { try { const stats = await fs.stat(filepath); return stats.isFile() && stats.size > 0; } catch { return false; } } /** * List all output files with metadata * @param {number} limit - Maximum number of results (default: 20) * @param {string} sortBy - Sort by "date" (default) or "size" * @returns {Promise<Array>} */ async listOutputs(limit = 20, sortBy = 'date') { try { const files = await fs.readdir(this.outputsDir); const jsonFiles = files.filter(f => f.endsWith('.json')); const fileStats = await Promise.all( jsonFiles.map(async (filename) => { const filepath = path.join(this.outputsDir, filename); const stats = await fs.stat(filepath); // Try to find associated command file const cmdFilename = filename .replace('result_', 'cmd_') .replace('.json', '.txt'); const cmdPath = path.join(this.commandsDir, cmdFilename); let associatedCommand = null; try { await fs.access(cmdPath); associatedCommand = cmdPath; } catch {} return { filename, path: filepath, size_mb: (stats.size / (1024 * 1024)).toFixed(2), created_at: stats.birthtime.toISOString(), associated_command: associatedCommand }; }) ); // Sort if (sortBy === 'size') { fileStats.sort((a, b) => parseFloat(b.size_mb) - parseFloat(a.size_mb)); } else { fileStats.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); } return fileStats.slice(0, limit); } catch (error) { throw new Error(`Failed to list outputs: ${error.message}`); } } /** * Clean up old files older than specified days * @param {number} daysOld - Delete files older than this many days (default: 7) * @returns {Promise<Object>} Count of deleted files */ async cleanOldFiles(daysOld = 7) { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysOld); const dirs = [this.commandsDir, this.outputsDir, this.tempDir]; let deletedCount = 0; try { for (const dir of dirs) { try { const files = await fs.readdir(dir); for (const file of files) { const filepath = path.join(dir, file); const stats = await fs.stat(filepath); if (stats.birthtime < cutoffDate) { await fs.unlink(filepath); deletedCount++; } } } catch (error) { // Directory might not exist yet, skip } } return { deleted_count: deletedCount }; } catch (error) { throw new Error(`Failed to clean old files: ${error.message}`); } } /** * Get storage statistics * @returns {Promise<Object>} */ async getStats() { try { const getDirectorySize = async (dir) => { try { const files = await fs.readdir(dir); let totalSize = 0; for (const file of files) { const filepath = path.join(dir, file); const stats = await fs.stat(filepath); if (stats.isFile()) { totalSize += stats.size; } } return totalSize; } catch { return 0; } }; const commandsSize = await getDirectorySize(this.commandsDir); const outputsSize = await getDirectorySize(this.outputsDir); const tempSize = await getDirectorySize(this.tempDir); return { commands_mb: (commandsSize / (1024 * 1024)).toFixed(2), outputs_mb: (outputsSize / (1024 * 1024)).toFixed(2), temp_mb: (tempSize / (1024 * 1024)).toFixed(2), total_mb: ((commandsSize + outputsSize + tempSize) / (1024 * 1024)).toFixed(2) }; } catch (error) { throw new Error(`Failed to get storage stats: ${error.message}`); } } } export default StorageManager;

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/bsim0927/texas-solver-mcp-server'

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