Skip to content

fix: make the UIA focus watchdog opt-in - #400

Merged
Jeomon merged 1 commit into
mainfrom
fix/watchdog-opt-in
Aug 29, 2026
Merged

fix: make the UIA focus watchdog opt-in#400
Jeomon merged 1 commit into
mainfrom
fix/watchdog-opt-in

Conversation

@Jeomon

@Jeomon Jeomon commented Aug 29, 2026

Copy link
Copy Markdown
Member

Refs #332. Behaviour change: the UIA focus watchdog no longer starts by default.

Why

While working #332 I went looking for what the watchdog actually feeds, and the answer is: nothing. Its only consumer in production is Tree.on_focus_change, and that method in full debounces duplicate events and writes one DEBUG log line. _last_focus_event is read nowhere except its own debounce check, and set_structure_callback / set_property_callback are called from nowhere at all.

Git history explains it. There used to be a _on_structure_change doing the real work — mutating tree_state.interactive_nodes, adding nodes on ChildAdded, removing them on ChildRemoved, rebuilding subtrees on ChildrenInvalidated, updating them on ChildrenReordered. That was deleted, and the log-only _on_property_change went with it. What survives is the delivery mechanism with its payload removed.

So the current cost/benefit is a dedicated STA thread, a long-lived UIA event subscription, COM construction at import time (the hazard #398 had to guard), and exposure to the native access violation in PumpEvents that kills the entire server with no Python traceback — in exchange for a debug log line.

What changed

WINDOWS_MCP_WATCHDOG becomes opt-in: on, 1, true, yes or enabled starts it, unset or anything else leaves it off. manifest.json's user_config default flips to false, and its description no longer claims the watchdog "keeps the accessibility tree current" — untrue since the structure-change code was removed. README and CLAUDE.md updated to match.

Anyone relying on the watchdog can still turn it on, and #399's backoff fix means it behaves better if they do.

Verification

The accessibility tree is built on demand for every tool call, so nothing about tool behaviour depends on this. Confirmed against the real stdio server rather than assumed — initialize + tools/list under both settings:

                   env |  _watchdog_enabled() | threads in server
                 unset |                False | 32
                    on |                 True | 36
                   off |                False | -
                  true |                 True | -
                 false |                False | -

All 20 tools served either way, clean exit both times; the default genuinely does not start the thread, and the manifest's boolean maps through correctly in both directions.

Full suite: 604 passed.

On #332

This does not "fix" the crash so much as stop shipping the thing that causes it to every user by default, which for a component with no current function is the honest trade. If the tree-cache maintenance is ever restored, the watchdog can be turned back on — ideally on a polling loop rather than native callbacks, so an access violation in UIA cannot take the server down. I'd suggest keeping #332 open until then.

The watchdog no longer earns what it costs. Its only surviving consumer is
Tree.on_focus_change, which debounces the event and writes a debug log line;
nothing reads the focus state it tracks. The structure-change handling that
once maintained tree_state.interactive_nodes -- adding nodes on ChildAdded,
removing them on ChildRemoved, rebuilding subtrees on ChildrenInvalidated --
was removed earlier, and _on_property_change went with it.

Against a debug log line, running it costs a dedicated STA thread, a
long-lived UIA event subscription, COM construction at import time, and
exposure to a native access violation in the event pump that kills the whole
server with no Python traceback (#332).

WINDOWS_MCP_WATCHDOG now has to be set to on/1/true/yes/enabled to start it.
Unset leaves it off. The accessibility tree is built on demand for every tool
call regardless, so no tool behaviour changes: verified against the real
stdio server, which serves all 20 tools either way and runs with 4 fewer
threads when the watchdog is off.

manifest.json's user_config default flips to false to match, and its
description no longer claims the watchdog "keeps the accessibility tree
current", which has not been true since the structure-change code was
removed. README and CLAUDE.md updated likewise.

Refs #332
@Jeomon
Jeomon merged commit 83e17f6 into main Aug 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant