What happen
caveman-shrink (the MCP proxy that wraps an upstream MCP server and compresses tool
descriptions) never forwards SIGTERM/SIGINT to the upstream process it spawned. If
something tears down the caveman-shrink process directly with a signal (an MCP host
ending a session, disabling a tool, reloading config) instead of killing a whole
process group, the wrapped upstream server is left running, reparented to PID 1,
indefinitely.
src/mcp-servers/caveman-shrink/index.js only registers handlers for the child's own
exit/error events:
const upstream = spawn(args[0], args.slice(1), getSpawnOptions());
upstream.on('error', err => { ... });
upstream.on('exit', (code, signal) => { ... });
There is no process.on('SIGTERM', ...) / process.on('SIGINT', ...) on the parent to
forward the signal (or kill the child) before exiting.
Expected
Sending SIGTERM to the caveman-shrink process should also stop the upstream child it
spawned, not leave it running orphaned.
Repro
# fake long-running upstream (never exits on its own)
cat > /tmp/fake-upstream.js <<'EOF'
process.stdin.resume();
setInterval(() => {}, 1000000);
EOF
node src/mcp-servers/caveman-shrink/index.js node /tmp/fake-upstream.js &
WRAPPER_PID=$!
sleep 1
kill -TERM $WRAPPER_PID
sleep 1
ps -o pid,ppid,stat,args
Verified today in a clean node:20-alpine container against current main
(0d95a81): the wrapper process becomes a zombie, and
the fake-upstream process is still alive with PPID 1 (reparented, orphaned), never
having received a signal.
Platform
Version / install method
main @ 0d95a81d, ran the script directly from the repo (not through the packaged
installer).
Happy to send a PR (forward SIGTERM/SIGINT to the child, mirroring the existing
upstream.on('exit', ...) handling) if useful.
What happen
caveman-shrink (the MCP proxy that wraps an upstream MCP server and compresses tool
descriptions) never forwards SIGTERM/SIGINT to the upstream process it spawned. If
something tears down the caveman-shrink process directly with a signal (an MCP host
ending a session, disabling a tool, reloading config) instead of killing a whole
process group, the wrapped upstream server is left running, reparented to PID 1,
indefinitely.
src/mcp-servers/caveman-shrink/index.jsonly registers handlers for the child's ownexit/errorevents:There is no
process.on('SIGTERM', ...)/process.on('SIGINT', ...)on the parent toforward the signal (or kill the child) before exiting.
Expected
Sending SIGTERM to the caveman-shrink process should also stop the upstream child it
spawned, not leave it running orphaned.
Repro
Verified today in a clean
node:20-alpinecontainer against currentmain(0d95a81): the wrapper process becomes a zombie, and
the fake-upstream process is still alive with
PPID 1(reparented, orphaned), neverhaving received a signal.
Platform
caveman-shrink(not agent-specific)Version / install method
main@0d95a81d, ran the script directly from the repo (not through the packagedinstaller).
Happy to send a PR (forward SIGTERM/SIGINT to the child, mirroring the existing
upstream.on('exit', ...)handling) if useful.