Skip to content

Snapshot/WaitFor tree walk deadlocks Electron host apps (VS Code) → Windows kills the MCP host; need process exclusion + per-window UIA budget #383

Description

@Springer-Tim

Summary

Desktop.get_state()TreeService.get_state() walks the UI-Automation tree of the active window and every other top-level window (other_windows_handles). When one of those windows belongs to an Electron app whose main process is also hosting the MCP client (VS Code running Claude Code), the UIA traffic deadlocks that Electron main process. Windows then terminates it as hung (Event Log: Application Hang 1002 / WER AppHangB1 on Code.exe), which kills every session in the host — including the one that issued the Snapshot.

There is currently no way to keep specific processes/windows out of the walk (checked config.toml, all WINDOWS_MCP_* env vars, --tools/--exclude-tools).

Environment

  • windows-mcp 0.8.5 via uvx windows-mcp serve (stdio), Windows 10 Pro 19045
  • Host: VS Code 1.133 + Claude Code extension; target app: Chrome (Google Cloud console — a large DOM)
  • Three Code.exe AppHang terminations in one afternoon, each with a Snapshot(use_dom=True) or WaitFor(use_dom=True) call in flight.

Minimal reproduction (isolated, no MCP client involved)

# walk.py — run with:  uvx --from windows-mcp python walk.py <hwnd_of_a_VS_Code_window> 150
import os, sys, time
os.environ["WINDOWS_MCP_MAX_TREE_ELEMENTS"] = sys.argv[2]
from windows_mcp.desktop.service import Desktop
from windows_mcp import uia
d = Desktop(); hwnd = int(sys.argv[1])
node = uia.ControlFromHandle(hwnd)
t = time.perf_counter()
res = d.tree.get_nodes(hwnd, d.is_window_browser(node), 0, use_dom=False)
print("done", time.perf_counter() - t)

While it runs, a separate probe does SendMessageTimeout(hwnd, WM_NULL, ..., SMTO_ABORTIFHUNG, 500ms) every 250 ms and logs IsHungAppWindow(hwnd).

Result against a VS Code window (element budget only 150):

  • probe: ok=True ms=0 for ~5 s, then ok=False ms=~500 … then IsHungAppWindow=True
  • the walk never returned (killed at 60 s)
  • VS Code's main process stayed Responding=False for >6 minutes after the walker process was killed — it does not recover; WER eventually terminates it.

Same test after relaunching VS Code with Chromium's --disable-renderer-accessibility: walk returns in 0.1 s with 3 nodes, probe stays ok=True throughout. So the trigger is UIA property/cache requests into Electron's renderer accessibility tree; the element budget does not prevent it because the deadlock happens before the budget is reached.

Impact

Any user running Windows-MCP from an Electron-hosted client (VS Code, Cursor, Claude Desktop, etc.) can lose their whole editor/session on an ordinary Snapshot when the walk reaches the host's own windows. It presents as "the app I was automating froze VS Code", so it is hard to attribute.

Requests

  1. Process/window exclusion list — e.g. WINDOWS_MCP_EXCLUDE_PROCESSES=Code.exe,Cursor.exe (and/or [tree] exclude_processes in config.toml), applied in get_window_wise_nodes before get_nodes. Cheapest fix and lets users protect their host.
  2. Skip the MCP host's own process tree by default — the server can find its parent process chain; never walk windows that belong to it.
  3. Per-window UIA time budget in get_nodes (abandon a window after N seconds) so one pathological window can't wedge the whole capture. Note the current 1800 s idle before the client aborts.
  4. README warning for Electron hosts pointing at --disable-renderer-accessibility as the client-side mitigation.

Happy to test a build. Full write-up with timings available on request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions