Summary
get_window_state (and by extension the MCP tool that calls it) can hang indefinitely when targeting heavy webview-based apps on macOS 26. The tool never returns — there's no error, no timeout from cua-driver's side — so any agent driving this via the MCP server is stranded until the user force-quits the host process.
I've reproduced this with a downstream Swift app embedding CuaDriverServer via the mcp__operator-driver__* registry. Wrapping our side with a 60s outer timeout works around it, but the root cause is inside cua-driver.
Environment
- macOS 26.4.1 (25E253) on Apple Silicon
- cua-driver Swift package: tested on 0.1.5 AND 0.1.9 — reproduces on both
replayd (ScreenCaptureKit daemon) and WindowServer both present and healthy
- Accessibility + Screen Recording TCC granted
- Reproduces with our wrapper around
CuaDriverServer.CuaDriverMCPServer.make driven over StdioTransport
Two flavors observed
Flavor A — Safari with many tabs
- Symptom:
get_window_state(pid: <safari-pid>) hangs.
- During the hang:
com.apple.Safari.History pegs at 100%+, replayd at ~60% CPU, WindowServer at ~45%.
- Hypothesis: the AX query is blocked behind Safari's history subsystem, which is itself wedged. ScreenCaptureKit may also be involved if the tool grabs a screenshot internally.
Flavor B — Arc browser
- Symptom:
get_window_state(pid: <arc-pid>) hangs.
- During the hang:
replayd is NOT hot (<1% CPU). Helper at ~16% CPU. The MCP helper's main thread is parked in mach_msg2_trap waiting on IPC; the actual work is happening on a background thread.
sample of the helper shows the main thread idle in RunCurrentEventLoopInMode → mach_msg2_trap. The hung AX walk is on a separate thread.
- Hypothesis: the AX tree on Arc (Chromium-based, lots of nested webviews and subwindows) is large enough that whatever AX API is being walked synchronously never returns. Possibly a known issue with
kAXChildrenAttribute enumeration on heavyweight Electron-style apps.
Reproduction
Easiest path:
- Open Arc, navigate to any non-trivial page (Gmail, Twitter, LinkedIn — anything with rich DOM).
- From an agent (Claude / Codex / Pi) configured to use cua-driver as an MCP server, prompt: "Read what's on screen in Arc."
- Watch the agent decide to call
get_window_state(pid: <arc-pid>).
- The call never returns; the agent shows the tool as in-flight forever.
We've reproduced with no painted highlights, no attachments, no MCP adapters in front — direct cua-driver MCP server. The hang is independent of the agent runtime (saw it on both Claude and Codex CLIs).
Suggested fixes
- Add an internal timeout to
get_window_state (and probably get_accessibility_tree) — return a partial result or a clear error after N seconds rather than hanging.
- Cap the AX tree walk depth/breadth so a pathologically large tree (Arc, Electron apps) doesn't take unbounded time.
- Cancel-safe AX queries — current call doesn't seem to honor Swift
Task cancellation, which prevents callers from cleaning up.
Summary
get_window_state(and by extension the MCP tool that calls it) can hang indefinitely when targeting heavy webview-based apps on macOS 26. The tool never returns — there's no error, no timeout from cua-driver's side — so any agent driving this via the MCP server is stranded until the user force-quits the host process.I've reproduced this with a downstream Swift app embedding
CuaDriverServervia themcp__operator-driver__*registry. Wrapping our side with a 60s outer timeout works around it, but the root cause is inside cua-driver.Environment
replayd(ScreenCaptureKit daemon) andWindowServerboth present and healthyCuaDriverServer.CuaDriverMCPServer.makedriven overStdioTransportTwo flavors observed
Flavor A — Safari with many tabs
get_window_state(pid: <safari-pid>)hangs.com.apple.Safari.Historypegs at 100%+,replaydat ~60% CPU,WindowServerat ~45%.Flavor B — Arc browser
get_window_state(pid: <arc-pid>)hangs.replaydis NOT hot (<1% CPU). Helper at ~16% CPU. The MCP helper's main thread is parked inmach_msg2_trapwaiting on IPC; the actual work is happening on a background thread.sampleof the helper shows the main thread idle inRunCurrentEventLoopInMode→mach_msg2_trap. The hung AX walk is on a separate thread.kAXChildrenAttributeenumeration on heavyweight Electron-style apps.Reproduction
Easiest path:
get_window_state(pid: <arc-pid>).We've reproduced with no painted highlights, no attachments, no MCP adapters in front — direct cua-driver MCP server. The hang is independent of the agent runtime (saw it on both Claude and Codex CLIs).
Suggested fixes
get_window_state(and probablyget_accessibility_tree) — return a partial result or a clear error after N seconds rather than hanging.Taskcancellation, which prevents callers from cleaning up.