Summary
render-mcp leaks a ManagedProcess context on every LOVR respawn — dead contexts (with their pipe tasks and open log file) accumulate in the process-lifetime AsyncExitStack until the whole server shuts down.
Details
In agent-mcp-servers/render-mcp/render_mcp/__main__.py, each successful start_lovr_once does await self._stack.enter_async_context(ManagedProcess(...)) (~L219-221). self._stack is the process-lifetime AsyncExitStack from _serve (~L540).
When LOVR exits, _watch resets _lovr_started = False (~L229); a later start_xr re-enters a new ManagedProcess into the same stack. The previous ManagedProcess's finally (terminate + pipe-task cancel + sink close) never runs until whole-process shutdown.
Consequence
Across N LOVR restarts, N-1 dead ManagedProcess contexts accumulate, each holding two leaked _forward pipe tasks and an open log-file handle. Slow resource creep (the previous child is already exited, so no orphaned process).
Suggested fix
Don't park the per-launch ManagedProcess in the app-lifetime stack. Use a dedicated AsyncExitStack (or explicit __aexit__) per launch and close it inside _watch after the child exits, before allowing respawn.
Filed from an automated code-logic review. Confidence: high. No assignee.
Summary
render-mcp leaks a
ManagedProcesscontext on every LOVR respawn — dead contexts (with their pipe tasks and open log file) accumulate in the process-lifetimeAsyncExitStackuntil the whole server shuts down.Details
In
agent-mcp-servers/render-mcp/render_mcp/__main__.py, each successfulstart_lovr_oncedoesawait self._stack.enter_async_context(ManagedProcess(...))(~L219-221).self._stackis the process-lifetimeAsyncExitStackfrom_serve(~L540).When LOVR exits,
_watchresets_lovr_started = False(~L229); a laterstart_xrre-enters a newManagedProcessinto the same stack. The previousManagedProcess'sfinally(terminate + pipe-task cancel + sink close) never runs until whole-process shutdown.Consequence
Across N LOVR restarts, N-1 dead
ManagedProcesscontexts accumulate, each holding two leaked_forwardpipe tasks and an open log-file handle. Slow resource creep (the previous child is already exited, so no orphaned process).Suggested fix
Don't park the per-launch
ManagedProcessin the app-lifetime stack. Use a dedicatedAsyncExitStack(or explicit__aexit__) per launch and close it inside_watchafter the child exits, before allowing respawn.Filed from an automated code-logic review. Confidence: high. No assignee.