UNPKG

@civic/nexus-bridge

Version:

Stdio <-> HTTP/SSE MCP bridge with Civic auth handling

83 lines 3 kB
/** * clientDetection.ts * * Utility for detecting client disconnection using MCP protocol's ping mechanism. * This helps bridge detect when the client process has closed even when the stdio * transport doesn't notify about disconnection. */ import { messageFromError } from '../utils.js'; import { logger } from './logger.js'; /** * Monitors client connection by using MCP ping mechanism */ export class ClientConnectionMonitor { server; pingInterval = null; pingFailCount = 0; isRunning = false; options; /** * Creates a new monitor for detecting client disconnection */ constructor(server, options) { this.server = server; this.options = { maxPingFailures: options?.maxPingFailures ?? 3, pingIntervalMs: options?.pingIntervalMs ?? 10000, pingTimeoutMs: options?.pingTimeoutMs ?? 5000, onDisconnection: options?.onDisconnection ?? (() => { logger.info("Client disconnection detected, but no handler was provided"); }) }; } /** * Start monitoring for client disconnection */ start() { // Prevent multiple monitors if (this.isRunning) { this.stop(); } this.isRunning = true; this.pingFailCount = 0; // Set up ping interval this.pingInterval = setInterval(async () => { try { logger.debug("[ClientMonitor] Sending ping to detect client connection"); // Use the ping utility built into the MCP protocol await this.server.ping(); // Reset failure count on successful ping if (this.pingFailCount > 0) { logger.info("[ClientMonitor] Client ping succeeded after previous failures"); this.pingFailCount = 0; } } catch (error) { this.pingFailCount++; logger.warn(`[ClientMonitor] Client ping failed (${this.pingFailCount}/${this.options.maxPingFailures}):`, messageFromError(error)); // If we've reached the failure threshold, assume client disconnection if (this.pingFailCount >= this.options.maxPingFailures) { logger.error("[ClientMonitor] Client appears to be disconnected after multiple ping failures"); this.stop(); // Notify about disconnection this.options.onDisconnection(); } } }, this.options.pingIntervalMs); // Clean up on process exit process.once('exit', () => { this.stop(); }); } /** * Stop monitoring for client disconnection */ stop() { if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = null; } this.isRunning = false; } } //# sourceMappingURL=clientDetection.js.map