fix(mcp-server): add graceful shutdown to prevent zombie processes (#477)

* fix(mcp-server): add graceful shutdown to prevent zombie processes

Add lifecycle handlers to properly exit the MCP server when the parent
application closes:

- Listen for stdin close/end events (primary method for all platforms)
- Handle SIGINT/SIGTERM signals
- Handle stdout broken pipe errors
- Export shutdown() function from http-server to clean up resources

* chore(mcp-server): bump version to 0.1.11
This commit is contained in:
Dayuan Jiang
2025-12-31 18:38:20 +09:00
committed by GitHub
parent 03ac9a79de
commit 7bdc1fe612
3 changed files with 33 additions and 2 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@next-ai-drawio/mcp-server",
"version": "0.1.10",
"version": "0.1.11",
"description": "MCP server for Next AI Draw.io - AI-powered diagram generation with real-time browser preview",
"type": "module",
"main": "dist/index.js",

View File

@@ -127,7 +127,12 @@ function cleanupExpiredSessions(): void {
}
}
setInterval(cleanupExpiredSessions, 5 * 60 * 1000)
const cleanupIntervalId = setInterval(cleanupExpiredSessions, 5 * 60 * 1000)
export function shutdown(): void {
clearInterval(cleanupIntervalId)
stopHttpServer()
}
export function getServerPort(): number {
return serverPort

View File

@@ -39,6 +39,7 @@ import {
getState,
requestSync,
setState,
shutdown,
startHttpServer,
waitForSync,
} from "./http-server.js"
@@ -618,6 +619,31 @@ server.registerTool(
},
)
// Graceful shutdown handler
let isShuttingDown = false
function gracefulShutdown(reason: string) {
if (isShuttingDown) return
isShuttingDown = true
log.info(`Shutting down: ${reason}`)
shutdown()
process.exit(0)
}
// Handle stdin close (primary method - works on all platforms including Windows)
process.stdin.on("close", () => gracefulShutdown("stdin closed"))
process.stdin.on("end", () => gracefulShutdown("stdin ended"))
// Handle signals (may not work reliably on Windows)
process.on("SIGINT", () => gracefulShutdown("SIGINT"))
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"))
// Handle broken pipe (writing to closed stdout)
process.stdout.on("error", (err) => {
if (err.code === "EPIPE" || err.code === "ERR_STREAM_DESTROYED") {
gracefulShutdown("stdout error")
}
})
// Start the MCP server
async function main() {
log.info("Starting MCP server for Next AI Draw.io (embedded mode)...")