Skip to content

tool-server.cjs is orphaned (reparented to launchd) when the argent mcp process that spawned it exits #327

@sebryu

Description

@sebryu

Environment: @swmansion/argent 0.11.0, Node v24.12.0 (nvm), macOS 26.5, Apple Silicon

Bug

When the argent mcp process that first spawns tool-server.cjs exits (e.g. its MCP client is a short-lived headless run that finishes), the tool-server does not exit. It re-parents to launchd (ppid 1) and runs indefinitely. There appears to be no parent-death detection (stdin EOF / ppid watchdog) and no idle timeout.

Repro (verified)

  1. Ensure no tool-server.cjs start process is running:
    ps -Ao pid,ppid,command | grep '[t]ool-server.cjs' → empty.
  2. In a directory whose .mcp.json registers argent as a stdio server:
    {"mcpServers":{"argent":{"type":"stdio","command":"argent","args":["mcp"]}}}
  3. Run any short-lived MCP client from that directory, e.g.:
    echo "say hi" | claude -p --model haiku --output-format text
  4. After the client exits: ps -Ao pid,ppid,command | grep '[t]ool-server.cjs' shows a tool-server with ppid 1 that never exits.

Why it's intermittent

If a tool-server already exists, a new argent mcp reuses it and exits cleanly — no leak. Only the client that first spawns the tool-server orphans it.

Additionally, spawn is not mutually exclusive: multiple argent mcp processes starting concurrently each spawn their own tool-server (we observed 4 simultaneous spawns within the same second after killing the existing one). Combined with the missing cleanup, automation that repeatedly launches short-lived MCP clients accumulates orphans over time.

Real-world impact

An automation daemon on my machine invoked headless claude -p ~2×/minute in a project with argent configured. Within hours: 123 orphaned tool-servers and, with simulators in use, 330 concurrent simctl/CoreSimulator processes, load average 445, memory exhausted — the machine was barely usable (~30% system CPU from simctl spawning alone).

With no simulator booted an orphan idles cheaply (it just never exits); the simctl storm occurs when simulators are in use.

Suggested fixes

  • tool-server exits when its last client disconnects (stdin/IPC EOF), and/or watches process.ppid and exits when re-parented to 1, and/or an idle TTL;
  • argent mcp reaps the tool-server it spawned on shutdown;
  • serialize tool-server spawn (lockfile / port probe) so concurrent clients can't each create one.

Any one of the first group would cap the damage; spawn serialization would make the leak self-limiting even if cleanup fails.

Workaround for anyone hitting this: kill orphans with
for pid in $(ps -Ao pid,ppid,command | awk '/argent\/dist\/tool-server/ && $2 == 1 {print $1}'); do kill $pid; done
and pass --strict-mcp-config --mcp-config '{"mcpServers":{}}' to headless claude -p runs that don't need tools.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions