UNPKG

mcp-chain-of-draft-server

Version:

A Model Context Protocol server which provides Chain of Draft style thinking

103 lines (102 loc) 3.49 kB
import { logger } from "./logging.js"; export class InMemorySessionStorage { constructor() { this.sessions = new Map(); } async get(sessionId) { const session = this.sessions.get(sessionId); if (session) { // Deep clone to prevent direct mutation return structuredClone(session); } return null; } async set(sessionId, data) { this.sessions.set(sessionId, structuredClone(data)); } async delete(sessionId) { this.sessions.delete(sessionId); } async cleanup(maxAge) { const now = Date.now(); for (const [sessionId, session] of this.sessions.entries()) { if (now - session.metadata.lastAccessed.getTime() > maxAge) { this.sessions.delete(sessionId); logger.info(`Cleaned up expired session: ${sessionId}`); } } } } export class SessionManager { constructor(storage, options = {}) { this.cleanupInterval = null; this.storage = storage; this.maxSessionAge = options.maxSessionAge || 24 * 60 * 60 * 1000; // 24 hours this.maxSessionSize = options.maxSessionSize || 5 * 1024 * 1024; // 5MB if (options.cleanupIntervalMs) { this.startCleanupInterval(options.cleanupIntervalMs); } } startCleanupInterval(intervalMs) { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } this.cleanupInterval = setInterval(() => { this.cleanup().catch(err => { logger.error('Session cleanup failed:', err); }); }, intervalMs); } async getSession(sessionId) { const session = await this.storage.get(sessionId); if (!session) { return this.createSession(sessionId); } // Update last accessed time session.metadata.lastAccessed = new Date(); await this.storage.set(sessionId, session); return session; } async createSession(sessionId) { const session = { data: {}, // Initialize with empty data metadata: { created: new Date(), lastAccessed: new Date(), size: 0, version: 1 } }; await this.storage.set(sessionId, session); logger.info(`Created new session: ${sessionId}`); return session; } async updateSession(sessionId, data) { const session = await this.getSession(sessionId); const newSize = this.calculateSize(data); if (newSize > this.maxSessionSize) { throw new Error(`Session size limit exceeded. Max: ${this.maxSessionSize}, Attempted: ${newSize}`); } session.data = data; session.metadata.lastAccessed = new Date(); session.metadata.size = newSize; await this.storage.set(sessionId, session); } async deleteSession(sessionId) { await this.storage.delete(sessionId); logger.info(`Deleted session: ${sessionId}`); } async cleanup() { await this.storage.cleanup(this.maxSessionAge); } calculateSize(data) { // Rough estimation of size in bytes return new TextEncoder().encode(JSON.stringify(data)).length; } destroy() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } } }