Skip to content

AX polling stalls focused Chrome on every navigation — 1-2s system-wide freeze #5

@achieverprince

Description

@achieverprince

Summary

The macos-mcp server holds an AXObserver registration on the focused
window via AXObserverAddNotification. When that focused window is
Google Chrome and the user navigates to a new page (or switches
tabs), Chrome rebuilds its accessibility tree, fires AX notifications,
and the MCP synchronously walks the new tree. Walking Chrome's AX tree
takes ~1-2 seconds and during that time the entire UI freezes
(WindowServer waits for AX RPCs).

This is reproducible on every page load, every time, when Chrome is
the focused app. Other apps freeze too but Chrome is the worst because
its AX tree is enormous (every tab, every DOM element exposed as AX).

Environment

  • macOS 26.4.1 (Build 25E253), Apple M2 (Mac14,5), 32 GB RAM
  • Claude Desktop (Responsible PID for both Python instances)
  • macos-mcp installed via Claude Extensions
    (ant.dir.gh.cursortouch.macos-mcp)
  • Chrome 147.0.7727.138

Symptoms

  • Whole system freezes for ~1-2s every time Chrome navigates
  • Only when Chrome has focus
  • No CPU spike in Activity Monitor at the OS level (one Python at ~50%
    of one core ≈ 4% on a 12-core machine — invisible at a glance)
  • Memory pressure stays green
  • Memory footprint grows unbounded over hours: 6.7 GB after ~18h,
    3.3 GB after ~2h on a second instance

Diagnostic Evidence

Captured via sudo spindump "Google Chrome" 60 100 -file .... The
heaviest non-idle stack across the system was a worker thread in the
macos-mcp Python process:

Thread 0x569c    cpu time 4.859s / 5.0s sample window
  pythread_wrapper
    thread_run
      method_vectorcall
        _PyEval_EvalFrameDefault
          func_vectorcall (_objc.cpython-312-darwin.so)
            ffi_call_int (libffi.dylib)
              ffi_call_SYSV
                _AXObserverAddNotificationAndCheckRemote (HIServices)
                  _AXObserverAddNotification
                    _AXMIGAddNotification
                      mach_msg → mach_msg2_trap (kernel)

The thread spent ~5 seconds out of 5 sampled (effectively all CPU
time) inside _AXObserverAddNotification on a single core. Chrome's
own browser process was idle (mach_msg waits) — it was waiting on
the AX RPC the MCP held open.

Process info:

  • Path: ~/Library/Application Support/Claude/Claude Extensions/ant.dir.gh.cursortouch.macos-mcp/.venv/bin/macos-mcp
  • Footprint: 6776 MB on 18.7-hour-old instance, 3346 MB on 2.3-hour-old instance
  • Parent: uv → Responsible: Claude

Hypothesis

The AX observer is registered against the system focused-window
notification, then on each notification callback the MCP walks the
target window's full AX tree synchronously. For Chrome, that tree
includes every tab and every accessible DOM node, which is huge.
Until the walk returns, AX RPCs from WindowServer back-pressure and
the user perceives a system freeze.

Suggested Fixes

  1. Throttle / debounce focus-change handlers. Don't react to every
    AX notification — coalesce and process at most once per N ms.

  2. Lazy AX walk. Don't proactively walk the focused window's tree
    on every focus/navigation. Walk on-demand only when an MCP tool
    call asks for it.

  3. Skip Chrome / browser apps. Maintain a deny-list of bundle IDs
    whose AX tree is known to be expensive. Or only walk top-level
    AXWindow and shallow children unless explicitly asked deeper.

  4. Run AX queries on a background thread with a strict timeout.
    Even today, the worker thread is doing AX work — but it appears
    to issue many small synchronous AX calls in a tight loop.
    Consolidating into batched async queries would let the system
    reclaim the AX RPC bus between batches.

Related

This compounds with #4
(multiple instances spawning at launch). On a machine with 4 instances
each polling AX on focused Chrome, the freezes stack and the system
becomes unusable.

Reproduction

  1. Install macos-mcp via Claude Extensions
  2. Start Claude Desktop, confirm macos-mcp is enabled
  3. Open Chrome, focus its window
  4. Open a complex site (gmail, github, twitter)
  5. Click any link, or hit Cmd+L and load a new URL
  6. Observe ~1-2s system-wide freeze immediately after page commit
  7. Repeat — freezes on every navigation, indefinitely

Disabling the macos-mcp extension (or killing the Python process)
stops the freezes immediately.

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