Process-group cleanup for stdio MCP servers to prevent orphan process storms (#10710)

This PR changes stdio MCP child processes to run in their own process
group
* Add guarded teardown in codex-rmcp-client: send SIGTERM to the group
first, then SIGKILL after a short grace period.
* Add terminate_process_group helper in process_group.rs.
* Add Unix regression test in process_group_cleanup.rs to verify wrapper
+ grandchild are reaped on client drop.

Addresses reported MCP process/thread storm: #10581
This commit is contained in:
Eric Traut
2026-02-06 21:26:36 -08:00
committed by GitHub
parent 4d52428fa2
commit 82c981cafc
5 changed files with 198 additions and 5 deletions

View File

@@ -117,6 +117,33 @@ pub fn kill_process_group_by_pid(_pid: u32) -> io::Result<()> {
Ok(())
}
#[cfg(unix)]
/// Send SIGTERM to a specific process group ID (best-effort).
///
/// Returns `Ok(true)` when SIGTERM was delivered to an existing group and
/// `Ok(false)` when the group no longer exists.
pub fn terminate_process_group(process_group_id: u32) -> io::Result<bool> {
use std::io::ErrorKind;
let pgid = process_group_id as libc::pid_t;
let result = unsafe { libc::killpg(pgid, libc::SIGTERM) };
if result == -1 {
let err = io::Error::last_os_error();
if err.kind() == ErrorKind::NotFound {
return Ok(false);
}
return Err(err);
}
Ok(true)
}
#[cfg(not(unix))]
/// No-op on non-Unix platforms.
pub fn terminate_process_group(_process_group_id: u32) -> io::Result<bool> {
Ok(false)
}
#[cfg(unix)]
/// Kill a specific process group ID (best-effort).
pub fn kill_process_group(process_group_id: u32) -> io::Result<()> {