Skip to main content
Glama

hypertool-mcp

healthMonitor.tsโ€ข8.15 kB
/** * Health monitoring system for MCP server connections */ import { EventEmitter } from "events"; import { Connection } from "./types.js"; /** * Server health states */ export enum HealthState { HEALTHY = "healthy", UNHEALTHY = "unhealthy", FAILED = "failed", } /** * Health check result */ export interface HealthCheckResult { serverId: string; serverName: string; state: HealthState; timestamp: Date; lastHealthyAt?: Date; error?: Error; } /** * Health monitoring configuration */ export interface HealthMonitorConfig { checkInterval: number; } /** * Health monitoring events */ export interface HealthMonitorEvents { stateChange: (result: HealthCheckResult, previousState?: HealthState) => void; } /** * Health monitor for tracking server availability */ export class HealthMonitor extends EventEmitter { private config: HealthMonitorConfig; private connections = new Map<string, Connection>(); private healthResults = new Map<string, HealthCheckResult>(); private checkTimer?: NodeJS.Timeout; private isRunning = false; constructor(config: Partial<HealthMonitorConfig> = {}) { super(); this.config = { checkInterval: 30000, // 30 seconds ...config, }; } /** * Add a connection to monitor */ addConnection(connection: Connection): void { this.connections.set(connection.serverName, connection); // Initialize health result const isConnected = connection.isConnected(); this.healthResults.set(connection.serverName, { serverId: connection.id, serverName: connection.serverName, state: isConnected ? HealthState.HEALTHY : HealthState.UNHEALTHY, timestamp: new Date(), lastHealthyAt: isConnected ? new Date() : undefined, }); // Listen for connection state changes connection.on("connected", () => { this.updateHealthStatus(connection.serverName, HealthState.HEALTHY); }); connection.on("disconnected", () => { this.updateHealthStatus(connection.serverName, HealthState.UNHEALTHY); }); connection.on("failed", (event) => { this.updateHealthStatus( connection.serverName, HealthState.FAILED, event.error ); }); } /** * Remove a connection from monitoring */ removeConnection(serverName: string): void { this.connections.delete(serverName); this.healthResults.delete(serverName); } /** * Start health monitoring */ start(): void { if (this.isRunning) { return; } this.isRunning = true; this.scheduleHealthChecks(); } /** * Stop health monitoring */ stop(): void { if (!this.isRunning) { return; } this.isRunning = false; if (this.checkTimer) { clearInterval(this.checkTimer); this.checkTimer = undefined; } } /** * Get health status for a specific server */ getHealthStatus(serverName: string): HealthCheckResult | undefined { return this.healthResults.get(serverName); } /** * Get health status for all servers */ getAllHealthStatus(): Map<string, HealthCheckResult> { return new Map(this.healthResults); } /** * Get list of healthy servers */ getHealthyServers(): string[] { return Array.from(this.healthResults.entries()) .filter(([, result]) => result.state === HealthState.HEALTHY) .map(([serverName]) => serverName); } /** * Get list of unhealthy servers */ getUnhealthyServers(): string[] { return Array.from(this.healthResults.entries()) .filter(([, result]) => result.state === HealthState.UNHEALTHY) .map(([serverName]) => serverName); } /** * Get list of failed servers */ getFailedServers(): string[] { return Array.from(this.healthResults.entries()) .filter(([, result]) => result.state === HealthState.FAILED) .map(([serverName]) => serverName); } /** * Perform health check on all connections */ async performHealthCheck(): Promise<Map<string, HealthCheckResult>> { const results = new Map<string, HealthCheckResult>(); const checkPromises = Array.from(this.connections.entries()).map( async ([serverName, connection]) => { const result = await this.checkConnectionHealth(connection); results.set(serverName, result); return result; } ); await Promise.allSettled(checkPromises); return results; } /** * Check health of a specific connection */ private async checkConnectionHealth( connection: Connection ): Promise<HealthCheckResult> { const previousResult = this.healthResults.get(connection.serverName); try { // Perform basic connectivity check const isConnected = connection.isConnected(); // Perform ping check if connected let pingResult = false; if (isConnected) { pingResult = await connection.ping(); } const healthy = isConnected && pingResult; let state: HealthState; if (healthy) { state = HealthState.HEALTHY; } else if (isConnected) { state = HealthState.UNHEALTHY; } else { state = HealthState.FAILED; } const result: HealthCheckResult = { serverId: connection.id, serverName: connection.serverName, state, timestamp: new Date(), lastHealthyAt: healthy ? new Date() : previousResult?.lastHealthyAt, }; this.processHealthResult(result, previousResult); return result; } catch (error) { const result: HealthCheckResult = { serverId: connection.id, serverName: connection.serverName, state: HealthState.FAILED, timestamp: new Date(), error: error as Error, lastHealthyAt: previousResult?.lastHealthyAt, }; this.processHealthResult(result, previousResult); return result; } } /** * Process health check result and emit appropriate events */ private processHealthResult( result: HealthCheckResult, previousResult?: HealthCheckResult ): void { // Update stored result this.healthResults.set(result.serverName, result); // Emit state change event only if state actually changed const previousState = previousResult?.state; if (previousState !== result.state) { this.emit("stateChange", result, previousState); } } /** * Update health status based on connection events */ private updateHealthStatus( serverName: string, state: HealthState, error?: Error ): void { const previousResult = this.healthResults.get(serverName); const result: HealthCheckResult = { serverId: previousResult?.serverId || "", serverName, state, timestamp: new Date(), error, lastHealthyAt: state === HealthState.HEALTHY ? new Date() : previousResult?.lastHealthyAt, }; this.processHealthResult(result, previousResult); } /** * Schedule periodic health checks */ private scheduleHealthChecks(): void { if (this.checkTimer) { clearInterval(this.checkTimer); } this.checkTimer = setInterval(async () => { if (this.isRunning) { await this.performHealthCheck(); } }, this.config.checkInterval); } /** * Override EventEmitter methods for type safety */ on<K extends keyof HealthMonitorEvents>( event: K, listener: HealthMonitorEvents[K] ): this; on(event: string, listener: (...args: any[]) => void): this; on(event: string, listener: any): this { return super.on(event, listener); } off<K extends keyof HealthMonitorEvents>( event: K, listener: HealthMonitorEvents[K] ): this; off(event: string, listener: (...args: any[]) => void): this; off(event: string, listener: any): this { return super.off(event, listener); } emit<K extends keyof HealthMonitorEvents>( event: K, ...args: Parameters<HealthMonitorEvents[K]> ): boolean; emit(event: string, ...args: any[]): boolean; emit(event: string, ...args: any[]): boolean { return super.emit(event, ...args); } }

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/toolprint/hypertool-mcp'

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