# SIGINT and SIGTERM handled for graceful shutdown

- **Pattern:** `ab-001845` (`mcp-server.error-resilience.graceful-shutdown`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001845
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001845)

## Why it matters

MCP servers running as child processes receive SIGINT when the user presses Ctrl+C and SIGTERM when the parent process shuts down. Without signal handlers, the Node.js process exits immediately (`SIGINT` default), potentially mid-write on an in-progress JSON-RPC response. The client receives a truncated or empty message and marks the server as crashed. CWE-404 (improper resource shutdown) covers this: open database connections, file handles, and HTTP keep-alives are abandoned rather than closed, which can leave the remote systems in inconsistent state.

## Severity rationale

Medium because abrupt termination corrupts in-progress responses and leaks resources, but the immediate impact is limited to the session in progress at shutdown time.

## Remediation

Register SIGINT and SIGTERM handlers that call `server.close()` before exiting. The SDK's `close()` drains in-flight messages and closes the transport cleanly.

```ts
// src/index.ts
async function shutdown() {
  console.error('Shutting down...')
  await server.close()  // closes transport, drains in-flight messages
  process.exit(0)
}

process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)

const transport = new StdioServerTransport()
await server.connect(transport)
```

If your server holds database connections or open file handles, close them inside `shutdown()` before calling `process.exit(0)`.

## Detection

- **ID:** `graceful-shutdown`
- **Severity:** `medium`
- **What to look for:** Count all signal handlers (SIGINT, SIGTERM) and cleanup routines. Enumerate whether the server completes in-flight requests before shutting down. Check for signal handlers (`process.on('SIGINT')`, `process.on('SIGTERM')`, `signal.signal()` in Python). When the server receives a shutdown signal, it should close the transport cleanly, finish any in-progress tool calls if possible, and release resources (database connections, file handles). Check that the server doesn't just call `process.exit()` immediately (which could corrupt in-progress responses).
- **Pass criteria:** The server handles SIGINT and SIGTERM, closes the transport cleanly, and exits. In-progress operations are either completed or cancelled gracefully. At least 1 signal handler must be registered for graceful shutdown.
- **Fail criteria:** No signal handlers (abrupt termination may corrupt in-progress responses), or signal handlers that call `process.exit(0)` immediately without cleanup.
- **Skip (N/A) when:** All checks skip when no MCP server is detected.

- **Cross-reference:** For timeout handling during shutdown, see `timeout-handling`.
- **Detail on fail:** `"No SIGINT/SIGTERM handlers — server will be killed abruptly, potentially corrupting in-progress responses or leaving resources open"` or `"Signal handler calls process.exit(0) immediately without closing the transport or cleaning up database connections"`
- **Remediation:** Handle shutdown signals gracefully:

  ```ts
  // src/index.ts — graceful shutdown
  process.on('SIGINT', async () => { await server.close(); process.exit(0) })
  ```

  ```ts
  const server = new McpServer({ name: 'my-server', version: '1.0.0' })
  const transport = new StdioServerTransport()

  async function shutdown() {
    console.error('Shutting down...')
    await server.close()  // closes transport and cleans up
    process.exit(0)
  }

  process.on('SIGINT', shutdown)
  process.on('SIGTERM', shutdown)

  await server.connect(transport)
  ```

  ```python
  import signal
  import asyncio

  async def shutdown(server):
      await server.close()

  loop = asyncio.get_event_loop()
  loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(shutdown(server)))
  loop.add_signal_handler(signal.SIGTERM, lambda: asyncio.create_task(shutdown(server)))
  ```

---

## External references

- cwe CWE-404
- iso-25010 reliability

Taxons: operational-readiness, error-resilience

HTML version: https://auditbuffet.com/patterns/ab-001845
