Skip to content

v1.4.2 — 98× faster where it was slowest, and the promise re-proved

Choose a tag to compare

@desteny-dev desteny-dev released this 01 Aug 22:43
· 12 commits to main since this release

Finding a control again after the page moved

When a ref goes stale — a web page re-renders, a list reorders — the server looks the control up again by what it was. That lookup walked the window's tree from Python, one COM round trip per node. 1.4.0 made it measure itself. These are the numbers it produced, on real windows:

walking the tree asking UI Automation
Claude (Electron) 1.278s 0.013s
ChatGPT 0.589s 0.023s
a browser tab 0.709s 0.041s
a small dialog 0.021s 0.021s — already fast

UI Automation can run the same search inside the application that owns the window, in one call. So it is asked first whenever the element had an automation id. Same answer in every case measured; only the time changed.

The cost, stated plainly: when the control really is gone, the search now tries UIA and then walks — so the miss case is about 5% slower, 2.19s → 2.31s on the worst window. That is the right trade. A ref goes stale far more often than it disappears, and the seconds being saved are seconds the person's screen is held.

The fallback walk is breadth-first now. Depth-first spent its whole 4000-node budget on the first deep branch it happened to enter; a control that moved in a re-render is almost always still near where it was. And when the search does run out of budget, the error now says so — "cut off at N nodes before it could look everywhere" — instead of implying the control is gone.

Worth recording: the first version of this was silently broken and measured as "no improvement at all". _AutomationClient is not exported at uiautomation's package level, the call raised, the error was swallowed, and the fallback produced identical timings. It was one sentence away from being reverted as a bad idea. Measuring the idea and measuring your own typo look exactly the same from the outside. When a change measures as doing nothing, first check that it ran.

"No network" — the one promise, re-proved on the file you install

A fair question was put to this project: the release-check workflow added in 1.4.1 reaches the network, and this project promises the opposite. How do those go together?

They go together because the promise is about the server — the thing that runs on your PC. But "it's fine, that's different" is an argument, and this project prefers proof. So that workflow now finishes by proving the promise on the only code that matters: it reads every Python file inside the published package and fails the release if any of them imports socket, urllib, http, ssl, requests, or anything else that can open a connection. On the shipped bytes, not on the repository.

SECURITY.md now draws the line exactly, instead of leaving "no network" to do work it cannot do:

what it does where it runs
scripts/CHECK-FOR-UPDATES asks GitHub whether a newer version exists; downloads only after you say yes and only after the SHA-256 matches on your machine — when you start it
.github/workflows/release-check.yml compares a published release with its source on GitHub's machines

Neither ships. The extension contains server.py, overlay.py, manifest.json, requirements.txt and lib/ — and a .mcpb is a zip, so you can list it yourself.

A test that failed at random, fixed rather than re-run

The stress test required the window count to be identical across three reads. That is not a property of the server — it is a claim that nobody opened or closed anything for a second and a half. Notifications appear, tooltips come and go. It failed on a different Python each time.

A test that fails at random gets ignored, and then it is not there on the day it matters — the same argument this project makes about a guard that refuses correct work. It now checks the narrower thing that is actually about the server: a window present in all three reads must be described identically. Churn is reported, not failed on.


Two downloads — take the one for your AI

Download For How
pc-screen-control.mcpb Claude Desktop Settings → Extensions → Advanced → Install extension
pc-screen-control-gpt.zip ChatGPT desktop, Codex, Cursor, VS Code, Cline, Zed Extract, double-click INSTALL-FOR-GPT.bat, restart

Same server.py, same bundled libraries, same 34 tools. Only the wrapper differs.

Install

Claude: remove the old version first (Settings → Extensions), quit Claude completely including the tray icon, install the .mcpb, start twice. Run self_test — it should report 1.4.2, all green.

ChatGPT / Codex: extract the zip, double-click INSTALL-FOR-GPT.bat, restart ChatGPT.

Needs Windows and Python 3.9+ with "Add python.exe to PATH" ticked.

Verify the downloads (optional)

pc-screen-control.mcpb
  SHA-256: 8bc83fc05a653221a189588806125366ff36aa6bcacfc130deccd363bac04e32
  Size:    7769050 bytes

pc-screen-control-gpt.zip
  SHA-256: f50f9218de075f544d3c3beb7e42586ccf45b84dad73f8fa533ff7f137d03611
  Size:    7675318 bytes