Executor version
1.6.7
How do you run Executor?
Self-host (Docker)
Operating system
macOS (Apple Silicon)
Integration involved
No response
What happened
I have a workflow where I start ~hundreds of agents connecting to a single executor server. They connect, do some work, and exit. I noticed that the number of PIDs on the machine was growing without limit.
Now, clanker speaking:
Executor creates one scoped executor for each MCP session. The scoped executor owns resources such as Tool subprocesses and connection pools.
The MCP session store does not keep a reference to the scoped executor. When it removes an idle session, it closes the MCP server and execution engine. However, it cannot close the scoped executor. A Tool subprocess can stay alive after its session no longer exists.
We confirmed this behavior:
- Open an MCP session.
- Call a fake stdio Tool and record its process ID.
- Wait for Executor to remove the idle session.
- Confirm that the session returns HTTP 404.
- Confirm that the Tool process is still alive.
What you expected
Sessions which disconnect should release their resources in a timely manner back to the host.
Steps to reproduce
- Open an MCP session.
- Call a fake stdio Tool and record its process ID.
- Wait for Executor to remove the idle session.
- Confirm that the session returns HTTP 404.
- Confirm that the Tool process is still alive.
Diagnostics / logs
Fresh isolated self-host MCP session leak reproduction
data_dir=/var/folders/nc/xm6rz4hd1g5741fhmwfznf0w0000gn/T/executor-session-leak-repro-zOmzB3/data
session_id=284197b3-ae22-4808-8bdc-248e49c263ef
tool_pid=21007
session_after_idle_http_status=404
session_after_idle_body={"jsonrpc":"2.0","error":{"code":-32001,"message":"Session not found"},"id":null}
tool_process_alive_after_session_eviction=true
executor_log=/var/folders/nc/xm6rz4hd1g5741fhmwfznf0w0000gn/T/executor-session-leak-repro-zOmzB3/executor.log
Executor host log:
tsr.routes.ts (2:30) �[33m[UNRESOLVED_IMPORT] Warning:�[0m Could not resolve '@executor-js/react/console-routes' in tsr.routes.ts
�[38;5;246m╭�[0m�[38;5;246m─�[0m�[38;5;246m[�[0m tsr.routes.ts:2:31 �[38;5;246m]�[0m
�[38;5;246m│�[0m
�[38;5;246m2 │�[0m �[38;5;249mi�[0m�[38;5;249mm�[0m�[38;5;249mp�[0m�[38;5;249mo�[0m�[38;5;249mr�[0m�[38;5;249mt�[0m�[38;5;249m �[0m�[38;5;249m{�[0m�[38;5;249m �[0m�[38;5;249mc�[0m�[38;5;249mo�[0m�[38;5;249mn�[0m�[38;5;249ms�[0m�[38;5;249mo�[0m�[38;5;249ml�[0m�[38;5;249me�[0m�[38;5;249mR�[0m�[38;5;249mo�[0m�[38;5;249mu�[0m�[38;5;249mt�[0m�[38;5;249me�[0m�[38;5;249ms�[0m�[38;5;249m �[0m�[38;5;249m}�[0m�[38;5;249m �[0m�[38;5;249mf�[0m�[38;5;249mr�[0m�[38;5;249mo�[0m�[38;5;249mm�[0m�[38;5;249m �[0m"@executor-js/react/console-routes"�[38;5;249m;�[0m
�[38;5;240m │�[0m ─────────────────┬─────────────────
�[38;5;240m │�[0m ╰─────────────────── Module not found, treating it as an external dependency
�[38;5;246m───╯�[0m
VITE v8.0.8 ready in 486 ms
➜ Local: http://localhost:4897/
➜ Network: use --host to expose
[executor] generated a secret-encryption key at /var/folders/nc/xm6rz4hd1g5741fhmwfznf0w0000gn/T/executor-session-leak-repro-zOmzB3/data/secret.key. Set EXECUTOR_SECRET_KEY to manage it explicitly (and to keep secrets readable across data-dir changes).
[19:21:36.226] INFO (#40) http.span.1=79ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/auth/sign-in/email",
"http.status": 200,
}
[19:21:36.805] INFO (#42) http.span.2=570ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/mcp/servers",
"http.status": 200,
}
[19:21:36.836] INFO (#66) http.span.3=25ms: Sent HTTP response {
"http.method": "GET",
"http.url": "/api/tools",
"http.status": 200,
}
[19:21:36.861] INFO (#79) http.span.4=21ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/toolkits",
"http.status": 200,
}
[19:21:36.886] INFO (#90) http.span.5=21ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/toolkits/tk_7fk9cfvljx8mtiip6fv/connections",
"http.status": 200,
}
[19:21:36.911] INFO (#101) http.span.6=21ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/toolkits/tk_7fk9cfvljx8mtiip6fv/policies",
"http.status": 200,
}
[executor] MCP session mode {"clientCapabilities":null,"elicitationSupport":{"form":false,"url":false},"elicitationMode":"model","resumeEnabled":true}
[19:21:36.945] INFO (#112) http.span.7=30ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/mcp/toolkits/fresh-session-leak-repro",
"http.status": 200,
}
[19:21:37.006] INFO (#128) http.span.8=57ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/mcp/toolkits/fresh-session-leak-repro",
"http.status": 200,
}
[19:22:44.023] INFO (#151) http.span.9=9ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/mcp/toolkits/fresh-session-leak-repro",
"http.status": 404,
}
Before you submit
Executor version
1.6.7
How do you run Executor?
Self-host (Docker)
Operating system
macOS (Apple Silicon)
Integration involved
No response
What happened
I have a workflow where I start ~hundreds of agents connecting to a single executor server. They connect, do some work, and exit. I noticed that the number of PIDs on the machine was growing without limit.
Now, clanker speaking:
Executor creates one scoped executor for each MCP session. The scoped executor owns resources such as Tool subprocesses and connection pools.
The MCP session store does not keep a reference to the scoped executor. When it removes an idle session, it closes the MCP server and execution engine. However, it cannot close the scoped executor. A Tool subprocess can stay alive after its session no longer exists.
We confirmed this behavior:
What you expected
Sessions which disconnect should release their resources in a timely manner back to the host.
Steps to reproduce
Diagnostics / logs
Fresh isolated self-host MCP session leak reproduction
data_dir=/var/folders/nc/xm6rz4hd1g5741fhmwfznf0w0000gn/T/executor-session-leak-repro-zOmzB3/data
session_id=284197b3-ae22-4808-8bdc-248e49c263ef
tool_pid=21007
session_after_idle_http_status=404
session_after_idle_body={"jsonrpc":"2.0","error":{"code":-32001,"message":"Session not found"},"id":null}
tool_process_alive_after_session_eviction=true
executor_log=/var/folders/nc/xm6rz4hd1g5741fhmwfznf0w0000gn/T/executor-session-leak-repro-zOmzB3/executor.log
Executor host log:
tsr.routes.ts (2:30) �[33m[UNRESOLVED_IMPORT] Warning:�[0m Could not resolve '@executor-js/react/console-routes' in tsr.routes.ts
�[38;5;246m╭�[0m�[38;5;246m─�[0m�[38;5;246m[�[0m tsr.routes.ts:2:31 �[38;5;246m]�[0m
�[38;5;246m│�[0m
�[38;5;246m2 │�[0m �[38;5;249mi�[0m�[38;5;249mm�[0m�[38;5;249mp�[0m�[38;5;249mo�[0m�[38;5;249mr�[0m�[38;5;249mt�[0m�[38;5;249m �[0m�[38;5;249m{�[0m�[38;5;249m �[0m�[38;5;249mc�[0m�[38;5;249mo�[0m�[38;5;249mn�[0m�[38;5;249ms�[0m�[38;5;249mo�[0m�[38;5;249ml�[0m�[38;5;249me�[0m�[38;5;249mR�[0m�[38;5;249mo�[0m�[38;5;249mu�[0m�[38;5;249mt�[0m�[38;5;249me�[0m�[38;5;249ms�[0m�[38;5;249m �[0m�[38;5;249m}�[0m�[38;5;249m �[0m�[38;5;249mf�[0m�[38;5;249mr�[0m�[38;5;249mo�[0m�[38;5;249mm�[0m�[38;5;249m �[0m"@executor-js/react/console-routes"�[38;5;249m;�[0m
�[38;5;240m │�[0m ─────────────────┬─────────────────
�[38;5;240m │�[0m ╰─────────────────── Module not found, treating it as an external dependency
�[38;5;246m───╯�[0m
VITE v8.0.8 ready in 486 ms
➜ Local: http://localhost:4897/
➜ Network: use --host to expose
[executor] generated a secret-encryption key at /var/folders/nc/xm6rz4hd1g5741fhmwfznf0w0000gn/T/executor-session-leak-repro-zOmzB3/data/secret.key. Set EXECUTOR_SECRET_KEY to manage it explicitly (and to keep secrets readable across data-dir changes).
[19:21:36.226] INFO (#40) http.span.1=79ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/auth/sign-in/email",
"http.status": 200,
}
[19:21:36.805] INFO (#42) http.span.2=570ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/mcp/servers",
"http.status": 200,
}
[19:21:36.836] INFO (#66) http.span.3=25ms: Sent HTTP response {
"http.method": "GET",
"http.url": "/api/tools",
"http.status": 200,
}
[19:21:36.861] INFO (#79) http.span.4=21ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/toolkits",
"http.status": 200,
}
[19:21:36.886] INFO (#90) http.span.5=21ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/toolkits/tk_7fk9cfvljx8mtiip6fv/connections",
"http.status": 200,
}
[19:21:36.911] INFO (#101) http.span.6=21ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/api/toolkits/tk_7fk9cfvljx8mtiip6fv/policies",
"http.status": 200,
}
[executor] MCP session mode {"clientCapabilities":null,"elicitationSupport":{"form":false,"url":false},"elicitationMode":"model","resumeEnabled":true}
[19:21:36.945] INFO (#112) http.span.7=30ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/mcp/toolkits/fresh-session-leak-repro",
"http.status": 200,
}
[19:21:37.006] INFO (#128) http.span.8=57ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/mcp/toolkits/fresh-session-leak-repro",
"http.status": 200,
}
[19:22:44.023] INFO (#151) http.span.9=9ms: Sent HTTP response {
"http.method": "POST",
"http.url": "/mcp/toolkits/fresh-session-leak-repro",
"http.status": 404,
}
Before you submit