Skip to main content
Glama
services.ts8.08 kB
import { systemClock } from "@mcpx/toolkit-core/time"; import { MeterProvider } from "@opentelemetry/sdk-metrics"; import path from "path"; import { LunarLogger } from "@mcpx/toolkit-core/logging"; import { ConfigService } from "../config.js"; import { env } from "../env.js"; import { OAuthSessionManager, OAuthSessionManagerI, } from "../server/oauth-session-manager.js"; import { ExtendedClientBuilder } from "./client-extension.js"; import { ControlPlaneService } from "./control-plane-service.js"; import { DockerService } from "./docker.js"; import { MetricRecorder } from "./metrics.js"; import { OAuthConnectionHandler } from "./oauth-connection-handler.js"; import { PermissionManager } from "./permissions.js"; import { ServerConfigManager } from "./server-config-manager.js"; import { SessionsManager } from "./sessions.js"; import { SystemStateTracker } from "./system-state.js"; import { TargetClients } from "./target-clients.js"; import { TargetServerConnectionFactory } from "./target-server-connection-factory.js"; import { ConfigValidator } from "./config-validator.js"; import { AuditLogService } from "./audit-log/audit-log-service.js"; import { FileAuditLogPersistence } from "./audit-log/audit-log-persistence.js"; import { HubService } from "./hub.js"; import { UIConnections } from "./connections.js"; import { SetupManager } from "./setup-manager.js"; import { CatalogManager } from "./catalog-manager.js"; import { WebappBoundPayloadOf } from "@mcpx/webapp-protocol/messages"; import { buildUsageStatsPayload } from "./usage-stats-sender.js"; export interface ServicesOptions { hubUrl?: string; } export class Services { private _sessions: SessionsManager; private _targetClients: TargetClients; private _permissionManager: PermissionManager; private _systemStateTracker: SystemStateTracker; private _controlPlane: ControlPlaneService; private _metricsRecord: MetricRecorder; private _dockerService: DockerService; private _oauthSessionManager: OAuthSessionManagerI; private _hubService: HubService; private _config: ConfigService; private _auditLogService: AuditLogService; private _connections: UIConnections; private _setupManager: SetupManager; private _catalogManager: CatalogManager; private logger: LunarLogger; private initialized = false; constructor( config: ConfigService, meterProvider: MeterProvider, logger: LunarLogger, options: ServicesOptions = {}, ) { this._config = config; const systemStateTracker = new SystemStateTracker(systemClock, logger); this._systemStateTracker = systemStateTracker; const extendedClientBuilder = new ExtendedClientBuilder(config); this._dockerService = new DockerService( env.MITM_PROXY_CA_CERT_PATH, logger, ); const oauthSessionManager = new OAuthSessionManager( logger.child({ component: "OAuthSessionManager" }), config.getConfig().staticOauth, ); this._oauthSessionManager = oauthSessionManager; const serverConfigManager = new ServerConfigManager( path.resolve(env.SERVERS_CONFIG_PATH), logger.child({ component: "ServerConfigManager" }), ); const oauthConnectionHandler = new OAuthConnectionHandler( oauthSessionManager, extendedClientBuilder, logger.child({ component: "OAuthConnectionHandler" }), ); const connectionFactory = new TargetServerConnectionFactory( extendedClientBuilder, this._dockerService, logger.child({ component: "ConnectionFactory" }), ); const targetClients = new TargetClients( this._systemStateTracker, serverConfigManager, connectionFactory, oauthConnectionHandler, logger, ); this._targetClients = targetClients; this._setupManager = new SetupManager(targetClients, config, logger); this._catalogManager = new CatalogManager(logger); function extractUsageStats(): WebappBoundPayloadOf<"usage-stats"> { const systemState = systemStateTracker.export(); return buildUsageStatsPayload(systemState); } this._hubService = new HubService( logger, this._setupManager, this._catalogManager, config, targetClients, extractUsageStats, { hubUrl: options.hubUrl }, ); const sessionsManager = new SessionsManager(systemStateTracker, logger); this._sessions = sessionsManager; this._permissionManager = new PermissionManager(logger); this._metricsRecord = new MetricRecorder(meterProvider); this._controlPlane = new ControlPlaneService( systemStateTracker, targetClients, config, logger, ); const auditLogPersistence = new FileAuditLogPersistence( env.AUDIT_LOG_DIR, env.AUDIT_LOG_RETENTION_HOURS, systemClock, logger.child({ component: "AuditLogPersistence" }), ); this._auditLogService = new AuditLogService( systemClock, logger.child({ component: "AuditLogService" }), auditLogPersistence, ); this._connections = new UIConnections(logger); this.logger = logger; } async initialize(): Promise<void> { if (this.initialized) { return; } this._config.registerConsumer(this._permissionManager); this._config.registerConsumer(new ConfigValidator()); await this._targetClients.initialize(); await this._config.initialize(); // Set up audit logging for config changes this.setupAuditLogging(); this._hubService.addStatusListener((status) => { this.logger.debug("Hub connection status changed", status); }); await this._hubService.initialize(); this.initialized = true; } private setupAuditLogging(): void { // Subscribe to config changes to audit log them this._config.subscribe(async (snapshot) => { this._auditLogService.log({ eventType: "config_applied", payload: snapshot, }); }); } async shutdown(): Promise<void> { this.logger.info("Shutting down services..."); // Close all connections (including UI socket) this._connections.shutdown(); // Close all sessions await this._sessions.shutdown(); // Shutdown target clients await this._targetClients.shutdown(); // Shutdown audit log service await this._auditLogService.shutdown(); // Disconnect from Hub await this._hubService.shutdown(); this.logger.info("All services shut down successfully"); } ensureInitialized(): void { if (!this.initialized) { throw new Error("Services not initialized"); } } get sessions(): SessionsManager { this.ensureInitialized(); return this._sessions; } get targetClients(): TargetClients { this.ensureInitialized(); return this._targetClients; } get permissionManager(): PermissionManager { this.ensureInitialized(); return this._permissionManager; } get oauthSessionManager(): OAuthSessionManagerI { this.ensureInitialized(); return this._oauthSessionManager; } get systemStateTracker(): SystemStateTracker { this.ensureInitialized(); return this._systemStateTracker; } get metricRecorder(): MetricRecorder { this.ensureInitialized(); return this._metricsRecord; } get controlPlane(): ControlPlaneService { this.ensureInitialized(); return this._controlPlane; } get dockerService(): DockerService { this.ensureInitialized(); return this._dockerService; } get auditLog(): AuditLogService { this.ensureInitialized(); return this._auditLogService; } get hubService(): HubService { this.ensureInitialized(); return this._hubService; } get connections(): UIConnections { this.ensureInitialized(); return this._connections; } get setupManager(): SetupManager { this.ensureInitialized(); return this._setupManager; } get catalogManager(): CatalogManager { this.ensureInitialized(); return this._catalogManager; } get config(): ConfigService { this.ensureInitialized(); return this._config; } }

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/TheLunarCompany/lunar'

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