Skip to main content
Glama

MCP Bridge Server

storage.js5.85 kB
import { join } from 'path'; import { mkdir } from 'fs/promises'; import { RegistrationError, RegistrationErrorType } from './types.js'; /** * File-based registration storage */ export class FileStorage { constructor(options) { this.storage = options.storage; this.directory = options.directory; this.retentionDays = options.retention?.days || 30; this.maxRecords = options.retention?.maxRecords; } /** * Initialize storage */ async initialize() { try { // Create directory await mkdir(this.directory, { recursive: true }); // Clean up old records await this.cleanup(); } catch (error) { throw new RegistrationError(RegistrationErrorType.PERSISTENCE_FAILED, 'Failed to initialize registration storage', error); } } /** * Save registration record */ async save(record) { try { await this.storage.write(this.getRecordPath(record.id), record); // Check if we need to enforce max records limit if (this.maxRecords) { const records = await this.list(); if (records.length > this.maxRecords) { // Sort by last updated and remove oldest records.sort((a, b) => new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime()); // Delete excess records await Promise.all(records .slice(this.maxRecords) .map(r => this.delete(r.id))); } } } catch (error) { throw new RegistrationError(RegistrationErrorType.PERSISTENCE_FAILED, 'Failed to save registration record', error); } } /** * Get registration record */ async get(id) { try { return await this.storage.read(this.getRecordPath(id)); } catch (error) { if (error?.code === 'ENOENT') { return null; } throw new RegistrationError(RegistrationErrorType.PERSISTENCE_FAILED, 'Failed to read registration record', error); } } /** * List all registration records */ async list() { try { // Get all record files const files = await this.storage.read(this.directory); // Load each record const records = await Promise.all(files .filter(file => file.endsWith('.json')) .map(file => this.storage.read(join(this.directory, file)))); return records; } catch (error) { throw new RegistrationError(RegistrationErrorType.PERSISTENCE_FAILED, 'Failed to list registration records', error); } } /** * Delete registration record */ async delete(id) { try { await this.storage.delete(this.getRecordPath(id)); } catch (error) { if (error?.code !== 'ENOENT') { throw new RegistrationError(RegistrationErrorType.PERSISTENCE_FAILED, 'Failed to delete registration record', error); } } } /** * Clean up old records */ async cleanup() { try { // Get all records const records = await this.list(); const now = new Date(); // Delete old records await Promise.all(records .filter(record => { const age = (now.getTime() - new Date(record.lastUpdated).getTime()) / (24 * 60 * 60 * 1000); return age > this.retentionDays; }) .map(record => this.delete(record.id))); } catch (error) { // Log cleanup errors but don't throw console.error('Registration record cleanup failed:', error); } } /** * Get record file path */ getRecordPath(id) { return join(this.directory, `${id}.json`); } /** * Validate record data */ validateRecord(record) { // Basic structure validation if (!record || typeof record !== 'object') return false; if (!record.id || typeof record.id !== 'string') return false; if (!record.request || typeof record.request !== 'object') return false; if (!record.response || typeof record.response !== 'object') return false; if (!(record.created instanceof Date)) return false; if (!(record.lastUpdated instanceof Date)) return false; if (typeof record.attempts !== 'number') return false; if (!Array.isArray(record.history)) return false; // Request validation const request = record.request; if (!request.clientType || typeof request.clientType !== 'string') return false; if (!request.machineId || typeof request.machineId !== 'string') return false; if (!(request.timestamp instanceof Date)) return false; // Response validation const response = record.response; if (!response.registrationId || typeof response.registrationId !== 'string') return false; if (!response.state || typeof response.state !== 'string') return false; if (!(response.expiresAt instanceof Date)) return false; // History validation for (const entry of record.history) { if (!(entry.timestamp instanceof Date)) return false; if (!entry.state || typeof entry.state !== 'string') return false; } return true; } } //# sourceMappingURL=storage.js.map

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/glassBead-tc/SubspaceDomain'

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