UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

281 lines (280 loc) 10.1 kB
"use strict"; /** * @fileoverview Transport Service Registry - Clean Architecture Infrastructure * @version 1.0.0 * @since 2025-07-30 * @lastUpdated 2025-07-30 * @module TransportServiceRegistry * @description Registry for managing multiple transport services in Clean Architecture. * Provides protocol-based service resolution and lifecycle management. * @contributors Claude Code Agent * @dependencies ITransportService interface * @requirements SECURITY_001 (Transport Layer Integration) * @testCoverage Unit and integration tests for service registration and resolution */ Object.defineProperty(exports, "__esModule", { value: true }); exports.InvalidServiceError = exports.DuplicateProtocolError = exports.InvalidProtocolError = exports.TransportServiceNotFoundError = exports.TransportRegistryError = exports.TransportServiceRegistry = void 0; /** * Transport Service Registry Implementation * * @description Manages registration and resolution of transport services by protocol. * Ensures proper lifecycle management and configuration validation. * * @example * ```typescript * const registry = new TransportServiceRegistry(); * registry.register('sse', new SSETransportService()); * registry.register('stdio', new StdioTransportService()); * * const sseService = registry.resolve('sse'); * ``` * * @since 2025-07-30 * @author Claude Code Agent * @requirements SECURITY_001 (Transport Layer Integration) */ class TransportServiceRegistry { constructor() { this.services = new Map(); this.configurations = new Map(); this.healthStatus = new Map(); } /** * Register a transport service for a specific protocol */ register(protocol, service) { this.validateProtocol(protocol); this.validateService(service); // Store service and its configuration this.services.set(protocol, service); this.configurations.set(protocol, service.getConfiguration()); this.healthStatus.set(protocol, false); // Will be updated by health checks console.log(`🚀 Transport service registered: ${protocol}`); // Start health monitoring for the service this.startHealthMonitoring(protocol, service); } /** * Resolve transport service by protocol */ resolve(protocol) { const service = this.services.get(protocol); if (!service) { throw new TransportServiceNotFoundError(`Transport service not found for protocol: ${protocol}. Available: ${this.getAvailableProtocols().join(', ')}`); } // Check if service is healthy const isHealthy = this.healthStatus.get(protocol); if (!isHealthy) { console.warn(`⚠️ Transport service for ${protocol} may be unhealthy`); } return service; } /** * Get all available transport protocols */ getAvailableProtocols() { return Array.from(this.services.keys()).sort(); } /** * Check if protocol is supported */ isSupported(protocol) { return this.services.has(protocol); } /** * Get configuration for a specific protocol */ getConfiguration(protocol) { return this.configurations.get(protocol) || null; } /** * Get health status for a specific protocol */ isHealthy(protocol) { return this.healthStatus.get(protocol) || false; } /** * Get health status for all registered services */ getHealthStatus() { const status = {}; for (const [protocol, healthy] of this.healthStatus.entries()) { status[protocol] = healthy; } return status; } /** * Perform health check on all registered services */ async performHealthChecks() { const results = {}; for (const [protocol, service] of this.services.entries()) { try { const isHealthy = await service.healthCheck(); this.healthStatus.set(protocol, isHealthy); results[protocol] = isHealthy; if (isHealthy) { console.log(`✅ Transport service healthy: ${protocol}`); } else { console.warn(`⚠️ Transport service unhealthy: ${protocol}`); } } catch (error) { this.healthStatus.set(protocol, false); results[protocol] = false; console.error(`❌ Health check failed for ${protocol}:`, error); } } return results; } /** * Gracefully shut down all transport services */ async shutdown() { console.log(`🔄 Shutting down ${this.services.size} transport services...`); const shutdownPromises = []; for (const [protocol, service] of this.services.entries()) { const shutdownPromise = service .shutdown() .then(() => { console.log(`✅ Transport service shut down: ${protocol}`); }) .catch(error => { console.error(`❌ Error shutting down ${protocol}:`, error); }); shutdownPromises.push(shutdownPromise); } // Wait for all services to shut down (with timeout) await Promise.allSettled(shutdownPromises); // Clear registry this.services.clear(); this.configurations.clear(); this.healthStatus.clear(); console.log('🔄 All transport services shut down'); } /** * Get registry statistics */ getStatistics() { const totalServices = this.services.size; const healthyServices = Array.from(this.healthStatus.values()).filter(h => h).length; const protocols = this.getAvailableProtocols(); return { totalServices, healthyServices, unhealthyServices: totalServices - healthyServices, protocols, configurations: Array.from(this.configurations.values()), }; } /** * Validate protocol name */ validateProtocol(protocol) { if (!protocol || typeof protocol !== 'string') { throw new InvalidProtocolError('Protocol must be a non-empty string'); } if (protocol.includes(' ') || protocol.includes('/')) { throw new InvalidProtocolError('Protocol cannot contain spaces or slashes'); } if (this.services.has(protocol)) { throw new DuplicateProtocolError(`Protocol ${protocol} is already registered`); } } /** * Validate transport service */ validateService(service) { if (!service) { throw new InvalidServiceError('Service cannot be null or undefined'); } // Verify service implements required interface methods const requiredMethods = [ 'authenticate', 'authorize', 'routeToHandler', 'auditLog', 'healthCheck', 'getConfiguration', 'shutdown', ]; for (const method of requiredMethods) { if (typeof service[method] !== 'function') { throw new InvalidServiceError(`Service must implement method: ${method}`); } } } /** * Start health monitoring for a service */ startHealthMonitoring(protocol, service) { // Perform initial health check service .healthCheck() .then(isHealthy => { this.healthStatus.set(protocol, isHealthy); console.log(`🔍 Initial health check for ${protocol}: ${isHealthy ? 'healthy' : 'unhealthy'}`); }) .catch(error => { this.healthStatus.set(protocol, false); console.error(`❌ Initial health check failed for ${protocol}:`, error); }); // Set up periodic health checks (every 30 seconds) const healthCheckInterval = setInterval(async () => { try { const isHealthy = await service.healthCheck(); const wasHealthy = this.healthStatus.get(protocol); this.healthStatus.set(protocol, isHealthy); // Log status changes if (wasHealthy !== isHealthy) { console.log(`🔄 Health status changed for ${protocol}: ${isHealthy ? 'healthy' : 'unhealthy'}`); } } catch (error) { this.healthStatus.set(protocol, false); console.error(`❌ Periodic health check failed for ${protocol}:`, error); } }, 30000); // 30 seconds // Store interval for cleanup (in a real implementation, we'd need proper cleanup) // For now, this is a simplified version } } exports.TransportServiceRegistry = TransportServiceRegistry; /** * Transport Registry Error Types */ class TransportRegistryError extends Error { constructor(message) { super(message); this.name = 'TransportRegistryError'; } } exports.TransportRegistryError = TransportRegistryError; class TransportServiceNotFoundError extends TransportRegistryError { constructor(message) { super(message); this.name = 'TransportServiceNotFoundError'; } } exports.TransportServiceNotFoundError = TransportServiceNotFoundError; class InvalidProtocolError extends TransportRegistryError { constructor(message) { super(message); this.name = 'InvalidProtocolError'; } } exports.InvalidProtocolError = InvalidProtocolError; class DuplicateProtocolError extends TransportRegistryError { constructor(message) { super(message); this.name = 'DuplicateProtocolError'; } } exports.DuplicateProtocolError = DuplicateProtocolError; class InvalidServiceError extends TransportRegistryError { constructor(message) { super(message); this.name = 'InvalidServiceError'; } } exports.InvalidServiceError = InvalidServiceError;