import type { FastMCP } from 'fastmcp'; /** * Server lifecycle management utilities */ export interface ServerLifecycleHooks { onStart?: (server: FastMCP) => Promise; onStop?: (server: FastMCP) => Promise; onError?: (error: Error, server: FastMCP) => Promise; } /** * Manages the lifecycle of an MCP server with proper startup/shutdown handling */ export class ServerLifecycleManager { private server: FastMCP; private hooks: ServerLifecycleHooks; private isRunning: boolean = false; constructor(server: FastMCP, hooks: ServerLifecycleHooks = {}) { this.server = server; this.hooks = hooks; } /** * Start the server with lifecycle hooks */ async start(): Promise { if (this.isRunning) { throw new Error('Server is already running'); } try { if (this.hooks.onStart) { await this.hooks.onStart(this.server); } this.isRunning = true; } catch (error) { if (this.hooks.onError) { await this.hooks.onError(error as Error, this.server); } throw error; } } /** * Stop the server gracefully */ async stop(): Promise { if (!this.isRunning) { return; } try { if (this.hooks.onStop) { await this.hooks.onStop(this.server); } this.isRunning = false; } catch (error) { if (this.hooks.onError) { await this.hooks.onError(error as Error, this.server); } throw error; } } /** * Get the server instance */ getServer(): FastMCP { return this.server; } /** * Check if server is running */ isServerRunning(): boolean { return this.isRunning; } } /** * Setup graceful shutdown handlers for the server */ export function setupGracefulShutdown( manager: ServerLifecycleManager ): void { const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGQUIT']; signals.forEach(signal => { process.on(signal, async () => { console.log(`\nReceived ${signal}, shutting down gracefully...`); try { await manager.stop(); process.exit(0); } catch (error) { console.error('Error during shutdown:', error); process.exit(1); } }); }); }