Skip to content

Stdio MCP disconnect leaves auto-assigned lanes orphaned #47

@apireno

Description

@apireno

Symptom

In stdio MCP mode, every connection auto-creates a lane (`mcp:`) on `SESSION_START`. When the client disconnects, the lane stays around — by design — so HTTP MCP clients can reconnect with the same session id and rejoin their lane.

For stdio MCP, every connection is genuinely fresh — there's no reconnect case. A wrapper that opens a fresh `ClientSession` for each call (the canonical pattern when the agent doesn't want long-lived MCP state, e.g., HKUDS/CLI-Anything's non-daemon mode) leaves one orphan tab group per call.

Surfaced 2026-05-29 in HKUDS/CLI-Anything PR #308 review by Codex (P2 finding on commit b897659):

In the non-daemon path this still creates and initializes a fresh MCP client for every wrapper call. DOMShell 2's server creates an isolated lane for each MCP client and documents that disconnected clients leave their lane open; when session.domshell_lane_id is already set, the command is then routed to the old lane via group_id, leaving the newly-created default lane unused after every fs ls, act click, etc.

Reproduction

  1. From a non-daemon MCP client, open N stdio connections in sequence (each one a fresh `ClientSession`)
  2. On each connection, route the actual command to a known lane via `group_id`
  3. Observe Chrome's tab groups: N orphan groups accumulate even though no commands ran in their default lanes

Why HTTP and stdio differ

Transport Reconnect possible? Should default lane survive disconnect?
HTTP MCP Yes (session id in header — client can rejoin) Yes — current behavior is correct
Stdio MCP No (every `stdio_client` is a fresh process pair) No — orphan is pure leak

Fix directions

  1. Detect non-stdio session at disconnect. When a stdio transport's MCP session disconnects, close its auto-assigned lane unless the client used it for at least one command. Preserves HTTP reconnect semantics.
  2. Lazy lane materialization. Don't create `mcp:` lane on `SESSION_START`; create it on first command that doesn't specify `group_id`. Connections that route every command via `group_id` (the HKUDS pattern) never materialize a default lane.
  3. Explicit close-on-disconnect. Server-side: when stdio disconnect happens for a session whose lane has zero command activity, close the lane.

(2) is the cleanest — it's purely additive ("create lane only when needed") and doesn't change behavior for any existing flow.

Workaround on the integrator side

HKUDS/CLI-Anything can refactor to use a persistent `ClientSession` per harness session (their post-merge follow-up). Once a single connection serves all calls, the default lane materializes once and is reused. The DOMShell-side fix would make this refactor unnecessary.

Priority

Not blocking — the agent's documented best-practice ("CLEAN UP YOUR LANES WHEN YOU FINISH") prevents the leak when followed, and the Chrome tab-group count is generally bounded by user attention. But for wrappers that open many short-lived MCP sessions per workflow, the accumulation is real.

P2 / next-patch candidate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions