npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@skilljack/mcp-server-manager

v0.1.0

Published

MCP server lifecycle manager with health checks, auto-restart, and graceful shutdown

Downloads

43

Readme

@skilljack/mcp-server-manager

A loosely coupled lifecycle manager for MCP (Model Context Protocol) servers with health checks, auto-restart, and graceful shutdown capabilities.

Features

  • Lifecycle Management - Start, stop, restart servers with proper state tracking
  • Health Monitoring - Periodic ping-based health checks with configurable thresholds
  • Auto-Restart - Automatic recovery from crashes with exponential backoff
  • Graceful Shutdown - Clean shutdown with configurable timeouts and force-kill fallback
  • Two Transport Types - Support for stdio (child processes) and HTTP connections
  • Event System - Comprehensive events for monitoring server states
  • Dynamic Management - Add/remove servers at runtime

Installation

npm install @skilljack/mcp-server-manager

Peer Dependency: Requires @modelcontextprotocol/sdk ^1.0.0

Quick Start

import {
  ServerManager,
  createServerConfig,
  createStdioConfig,
} from '@skilljack/mcp-server-manager';

// Create a manager with server configurations
const manager = ServerManager.fromConfig({
  servers: [
    createServerConfig('my-server', createStdioConfig('npx', ['-y', '@modelcontextprotocol/server-everything']))
  ]
});

// Listen for events
manager.on('server:connected', (event) => {
  console.log(`Server ${event.serverName} connected`);
});

// Start all servers
await manager.start();

// Use the MCP client
const client = manager.getClient('my-server');
if (client) {
  const tools = await client.listTools();
}

// Graceful shutdown
await manager.shutdown();

Configuration

Server Configuration

interface ServerConfig {
  name: string;                        // Unique server identifier
  connection: ServerConnectionConfig;  // Stdio or HTTP config
  lifecycle?: LifecycleConfig;         // Optional lifecycle overrides
  autoStart?: boolean;                 // Auto-start with manager (default: true)
}

Stdio Connection (Child Process)

Spawns a child process and communicates via stdin/stdout pipes.

import { createStdioConfig } from '@skilljack/mcp-server-manager';

const config = createStdioConfig('npx', ['-y', '@modelcontextprotocol/server-everything'], {
  env: { DEBUG: 'true' },  // Optional environment variables
  cwd: '/path/to/dir',     // Optional working directory
});

HTTP Connection

Connects to an existing HTTP-based MCP server.

import { createHttpConfig } from '@skilljack/mcp-server-manager';

const config = createHttpConfig('http://localhost:3000', {
  headers: { 'Authorization': 'Bearer token' },  // Optional headers
});

Lifecycle Configuration

Override default lifecycle behavior per-server or globally:

interface LifecycleConfig {
  healthCheckEnabled?: boolean;       // Enable health checks (default: true)
  healthCheckIntervalMs?: number;     // Check interval (default: 30000)
  healthCheckTimeoutMs?: number;      // Check timeout (default: 5000)
  unhealthyThreshold?: number;        // Failures before unhealthy (default: 3)
  autoRestartEnabled?: boolean;       // Enable auto-restart (default: true)
  maxRestartAttempts?: number;        // Max restart tries (default: 5)
  restartBackoffBaseMs?: number;      // Backoff base delay (default: 1000)
  restartBackoffMaxMs?: number;       // Max backoff delay (default: 30000)
  shutdownTimeoutMs?: number;         // Graceful shutdown timeout (default: 10000)
}

Full Configuration Example

const manager = ServerManager.fromConfig({
  // Global defaults (optional)
  defaults: {
    healthCheckIntervalMs: 60000,
    maxRestartAttempts: 3,
  },
  servers: [
    {
      name: 'primary',
      connection: { type: 'stdio', command: 'node', args: ['server.js'] },
      autoStart: true,
    },
    {
      name: 'secondary',
      connection: { type: 'http', url: 'http://localhost:8080' },
      autoStart: false,
      lifecycle: {
        healthCheckEnabled: false,  // Override for this server
      },
    },
  ],
});

Server Lifecycle States

disconnected ──start()──▶ connecting ──success──▶ connected
                              │                       │
                              │                       │ health check failures
                              ▼                       ▼
                           failed ◀────────────── unhealthy
                              ▲                       │
                              │                       │ auto-restart
                              │                       ▼
                              └──max attempts──── restarting
                                                      │
                                                      │ success
                                                      ▼
                                                  connecting

Any state ──stop()──▶ stopped

| Status | Description | |--------|-------------| | disconnected | Initial state, not yet started | | connecting | Connection in progress | | connected | Successfully connected and healthy | | unhealthy | Health checks failing | | restarting | Auto-restart in progress | | failed | Max restart attempts exceeded | | stopped | Manually stopped |

Events

Lifecycle Events

| Event | Description | Key Properties | |-------|-------------|----------------| | server:connecting | Connection starting | serverName | | server:connected | Successfully connected | serverName, pid? | | server:connection-failed | Connection failed | serverName, error | | server:healthy | Health check passed | serverName, healthCheck | | server:unhealthy | Health checks failing | serverName, consecutiveFailures | | server:crashed | Process crashed | serverName, exitCode, signal, willRestart | | server:restarting | Restart initiated | serverName, attempt, maxAttempts, reason | | server:restart-succeeded | Restart successful | serverName, attempts, pid? | | server:restart-failed | All restart attempts failed | serverName, attempts, error | | server:status-changed | Any status change | serverName, previousStatus, newStatus | | server:stopped | Server stopped | serverName, graceful |

Manager Events

| Event | Description | Key Properties | |-------|-------------|----------------| | manager:ready | All auto-start servers started | serverCount | | manager:shutdown | Shutdown complete | graceful | | manager:state-snapshot | State snapshot emitted | servers (array of summaries) |

Event Usage

// Specific event
manager.on('server:connected', (event) => {
  console.log(`${event.serverName} connected with PID ${event.pid}`);
});

// All lifecycle events
manager.on('*', (event) => {
  console.log(`Event: ${event.type}`, event);
});

// All manager events
manager.on('manager:*', (event) => {
  console.log(`Manager event: ${event.type}`);
});

// Typed event helpers
manager.onLifecycleEvent('server:unhealthy', (event) => {
  console.log(`${event.serverName} unhealthy after ${event.consecutiveFailures} failures`);
});

manager.onManagerEvent('manager:ready', (event) => {
  console.log(`Manager ready with ${event.serverCount} servers`);
});

API Reference

ServerManager

Static Methods

// Create from config object
ServerManager.fromConfig(config: ManagerConfig, options?: ServerManagerOptions): ServerManager

// Create from config file
ServerManager.fromConfigFile(filePath: string, options?: ServerManagerOptions): Promise<ServerManager>

Instance Methods

// Lifecycle
start(): Promise<void>              // Start all auto-start servers
shutdown(): Promise<void>           // Stop all servers gracefully
startServer(name: string): Promise<void>
stopServer(name: string): Promise<void>
restartServer(name: string): Promise<void>

// State
getServerStatus(name: string): ServerStatus | undefined
getServerState(name: string): ServerStateSummary | undefined
getAllServerStates(): ServerStateSummary[]
isServerConnected(name: string): boolean
areAllServersConnected(): boolean

// Clients
getClient(name: string): Client | null
getConnectedClients(): Map<string, Client>

// Management
getServerNames(): string[]
getServerCount(): number
addServer(config: ServerConfig): void
removeServer(name: string): Promise<void>

// Events
emitStateSnapshot(): void

ServerStateSummary

interface ServerStateSummary {
  name: string;
  status: ServerStatus;
  healthy: boolean;
  timeInStatus: number;      // milliseconds
  pid?: number;
  lastLatencyMs?: number;    // last health check latency
  restartAttempts: number;
  error?: string;
}

Advanced Usage

Custom Logger

import { ServerManager, ConsoleLoggerFactory } from '@skilljack/mcp-server-manager';

const manager = ServerManager.fromConfig(config, {
  loggerFactory: new ConsoleLoggerFactory('debug'),
});

Using Core Components Directly

For advanced use cases, you can use the underlying components:

import {
  ServerLifecycle,
  ProcessManager,
  HealthMonitor,
  HttpConnection,
} from '@skilljack/mcp-server-manager';

// Create a single server lifecycle
const lifecycle = new ServerLifecycle(serverConfig, lifecycleDefaults);
lifecycle.on('*', (event) => console.log(event));
await lifecycle.start();

Retry Utilities

import { retry, withTimeout, calculateBackoff } from '@skilljack/mcp-server-manager';

// Retry an operation
const result = await retry(
  async () => fetchData(),
  { maxAttempts: 3, backoff: { baseMs: 1000, maxMs: 10000 } }
);

// Add timeout to a promise
const data = await withTimeout(fetchData(), 5000);

Architecture

ServerManager
    │
    ├── ServerLifecycle (one per server)
    │   ├── ProcessManager (stdio transport)
    │   │   └── Child Process (stdin/stdout pipes)
    │   │
    │   ├── HttpConnection (http transport)
    │   │
    │   ├── MCP Client
    │   │   └── Transport (StdioClientTransport or StreamableHTTPClientTransport)
    │   │
    │   └── HealthMonitor
    │       └── Periodic ping checks
    │
    └── Event Emitter
        └── Forwards all lifecycle events

License

MIT