Skip to main content
Glama

Google Cloud MCP Server

by krzko
session-manager.ts5.6 kB
/** * Session management utilities for MCP server security compliance * * Implements MCP specification 2025-06-18 security requirements: * - Secure session ID generation (non-deterministic) * - Session ID rotation and expiration * - Request verification without session-based auth * - Token audience validation */ import crypto from "crypto"; import { ISessionManager } from "./interfaces.js"; import { logger } from "./logger.js"; interface SessionInfo { id: string; created: Date; lastUsed: Date; rotationCount: number; metadata: Record<string, any>; } /** * Session manager for MCP security compliance */ class SessionManager implements ISessionManager { private sessions = new Map<string, SessionInfo>(); private readonly SESSION_LIFETIME_MS = 24 * 60 * 60 * 1000; // 24 hours private readonly MAX_ROTATION_COUNT = 10; private readonly CLEANUP_INTERVAL_MS = 60 * 60 * 1000; // 1 hour constructor() { // Periodically clean up expired sessions setInterval(() => { this.cleanupExpiredSessions(); }, this.CLEANUP_INTERVAL_MS); } /** * Generate a secure, non-deterministic session ID * Following MCP specification security requirements */ generateSecureSessionId(): string { // Use cryptographically secure random bytes const randomBytes = crypto.randomBytes(32); const timestamp = Date.now().toString(36); const randomString = randomBytes.toString("base64url"); // Combine timestamp and random data for uniqueness return `mcp_${timestamp}_${randomString}`; } /** * Create a new session with secure ID generation */ createSession(metadata: Record<string, any> = {}): string { const sessionId = this.generateSecureSessionId(); const now = new Date(); const sessionInfo: SessionInfo = { id: sessionId, created: now, lastUsed: now, rotationCount: 0, metadata: { ...metadata }, }; this.sessions.set(sessionId, sessionInfo); return sessionId; } /** * Rotate session ID for enhanced security */ rotateSessionId(currentSessionId: string): string | null { const session = this.sessions.get(currentSessionId); if (!session) { return null; } // Check rotation limits if (session.rotationCount >= this.MAX_ROTATION_COUNT) { this.sessions.delete(currentSessionId); return null; } // Generate new session ID const newSessionId = this.generateSecureSessionId(); const now = new Date(); // Create new session with updated info const newSession: SessionInfo = { ...session, id: newSessionId, lastUsed: now, rotationCount: session.rotationCount + 1, }; // Replace old session with new one this.sessions.delete(currentSessionId); this.sessions.set(newSessionId, newSession); return newSessionId; } /** * Validate session exists and is not expired */ validateSession(sessionId: string): boolean { const session = this.sessions.get(sessionId); if (!session) { return false; } const now = new Date(); const age = now.getTime() - session.created.getTime(); // Check if session has expired if (age > this.SESSION_LIFETIME_MS) { this.sessions.delete(sessionId); return false; } // Update last used time session.lastUsed = now; return true; } /** * Get session metadata */ getSessionMetadata(sessionId: string): Record<string, any> | null { const session = this.sessions.get(sessionId); return session ? { ...session.metadata } : null; } /** * Update session metadata */ updateSessionMetadata( sessionId: string, metadata: Record<string, any>, ): boolean { const session = this.sessions.get(sessionId); if (!session) { return false; } session.metadata = { ...session.metadata, ...metadata }; session.lastUsed = new Date(); return true; } /** * Invalidate a session */ invalidateSession(sessionId: string): boolean { return this.sessions.delete(sessionId); } /** * Clean up expired sessions */ cleanupExpiredSessions(): number { const now = new Date(); const expiredSessions: string[] = []; for (const [sessionId, session] of this.sessions.entries()) { const age = now.getTime() - session.created.getTime(); if (age > this.SESSION_LIFETIME_MS) { expiredSessions.push(sessionId); } } // Remove expired sessions for (const sessionId of expiredSessions) { this.sessions.delete(sessionId); } if (expiredSessions.length > 0) { logger.debug(`Cleaned up ${expiredSessions.length} expired sessions`); } return expiredSessions.length; } /** * Get session statistics */ getSessionStats(): { active: number; total: number; expired: number } { return { active: this.sessions.size, total: this.sessions.size, // This is simplified - in production would track total expired: 0, // This would be tracked separately in production }; } } // Singleton instance export const sessionManager = new SessionManager(); /** * Generate secure random bytes for various security purposes */ export function generateSecureRandomBytes(size: number): Buffer { return crypto.randomBytes(size); } /** * Generate secure random string suitable for tokens or IDs */ export function generateSecureRandomString(length: number = 32): string { const bytes = crypto.randomBytes(Math.ceil((length * 3) / 4)); return bytes.toString("base64url").substring(0, length); }

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/krzko/google-cloud-mcp'

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