Blog
shutdownManager.ts in mcp-mermaid codebase.

shutdownManager.ts in mcp-mermaid codebase.

In this article, we review shutdownManager.ts in mcp-mermaid codebase. We will look at:

  1. What is shutdownManager.ts?

  2. Functions defined.

What is shutdownManager.ts?

I found the following comment in mcp-mermaid/src/utils/shutdownManager.ts


/**
 * Centralized shutdown manager for handling cleanup and graceful shutdown
 */

So, this is basically used to cleanup and gracefully shutdown, but what exactly is getting cleaned up? To understand that we will have to pick some references from the mcp-mermaid codebase.

registerCleanup

Example 1:

In mcp-mermaid/src/server.ts, you will find the below code:

// Register server cleanup with shutdown manager
shutdownManager.registerCleanup(createServerCleanup(server));

Example 2:

In mcp-mermaid/src/services/stdio.ts, you will find the below code:

// Register transport cleanup
shutdownManager.registerCleanup(createTransportCleanup(transport));

But how is actual cleanup happening? If you take a closer look at the below code in mcp-mermaid/src/utils/httpServer.ts:

// Setup signal handlers only once
shutdownManager.setupSignalHandlers();

To understand how calling this once cleans up, you will have to understand the functions defined in ShutdownManager.

Functions defined

registerCleanup

export class ShutdownManager {
  private cleanupHandlers: Array<() => void | Promise<void>> = [];
  private isShuttingDown = false;

  /**
   * Register a cleanup handler
   */
  registerCleanup(handler: () => void | Promise<void>): void {
    this.cleanupHandlers.push(handler);
  }

cleanup

async cleanup(): Promise<void> {
    if (this.isShuttingDown) {
      Logger.warn("Shutdown already in progress, forcing exit...");
      process.exit(1);
    }

    this.isShuttingDown = true;
    Logger.info("🔄 Shutting down gracefully...");

    // Execute all cleanup handlers with timeout
    const cleanupPromises = this.cleanupHandlers.map(async (handler) => {
      try {
        await Promise.race([
          handler(),
          new Promise((_, reject) =>
            setTimeout(() => reject(new Error("Cleanup timeout")), 3000),
          ),
        ]);
      } catch (error) {
        Logger.error("Error during cleanup", error);
      }
    });

    await Promise.all(cleanupPromises);
    Logger.success("Cleanup completed");
    process.exit(0);
  }

setupSignalHandlers

/**
* Setup signal handlers for graceful shutdown
*/
setupSignalHandlers(): void {
  // Remove any existing listeners to avoid duplicates
  process.removeAllListeners("SIGINT");
  process.removeAllListeners("SIGTERM");
  
  process.once("SIGINT", this.cleanup.bind(this));
  process.once("SIGTERM", this.cleanup.bind(this));
}

setupSignalHandlers is setup once and calls the cleanup function when a process terminates, which in this case is, server I am assuming and the process events that are registered here are SIGINT and SIGTERM.

About me:

Hey, my name is Ramu Narasinga. I study codebase architecture in large open-source projects.

Email: ramu.narasinga@gmail.com

Want to learn from open-source? Solve challenges inspired by open-source projects.

References:

  1. https://github.com/hustcc/mcp-mermaid/blob/main/src/utils/shutdownManager.ts#L13

  2. https://github.com/hustcc/mcp-mermaid/blob/ee4b38216771306d48c2ed31f04ce3f2975cc97a/src/server.ts#L40

  3. https://github.com/hustcc/mcp-mermaid/blob/ee4b38216771306d48c2ed31f04ce3f2975cc97a/src/services/stdio.ts#L10