Skip to content

Releases: desteny-dev/pc-screen-control

v1.6.3 — the maker has a different name

Choose a tag to compare

@desteny-dev desteny-dev released this 06 Aug 13:40

NATHAN Development is now Desteny Development. Nothing in the program changed: same 34 tools, same behaviour, same nineteen test files. If you are on 1.6.2 there is no reason to hurry.

Why a release for a name

The name sits inside the package, in manifest.json. The moment it changed, the code and the published 1.6.2 disagreed about who made it - while both called themselves 1.6.2.

Two different files with the same version number is worse than any naming question: after that, nobody can say which one they are holding. The end check caught it and was right to; it compares what is published against the code at the tag, and it does not care that the difference was "only" a name.

So the versioning rule is unchanged. A rename is not a feature. It gets a number because the artefact changed, not because the software did.

What did not change

The repository keeps its name. pc-screen-control is the product, not the maker.

Commits made before the rename still carry the old one. The brand was renamed, the past was not - rewriting history to make it look as though this was always called Desteny would change every hash and invalidate the tags of four published releases, in exchange for a tidier-looking log.

Old links still work: GitHub redirects nathandevelopment/pc-screen-control to the new address.

Install

Claude Desktop - pc-screen-control.mcpb -> Settings -> Extensions -> Advanced -> Install extension. Remove the previous version first and quit Claude completely, tray icon included.

ChatGPT desktop, Codex, Cursor, VS Code, Cline, Zed - pc-screen-control-gpt.zip -> extract -> INSTALL-FOR-GPT.bat -> restart.

Windows and Python 3.9+ with "Add python.exe to PATH". Run self_test - it should report 1.6.3.

Checksums

pc-screen-control.mcpb      8240197 bytes
  SHA-256 e2d1c7085afdc718baf8f4621ebf20d68184ad5196750e179f162774a1be2d51
pc-screen-control-gpt.zip   8140706 bytes
  SHA-256 e1425e6e1b07af6055861bf41e83676cd037a76dcbfa35b51ffb12136769381e

v1.6.2 — the warning was in the wrong place, and nothing could have said so

Choose a tag to compare

@desteny-dev desteny-dev released this 06 Aug 09:26

Reported as "the frame sits in the top-left corner". Measured on the desktop it happened on: two monitors, 3840x2160 and 1080x1920, and the overlay's own bar windows were 1280x720 at the origin. Four of them, not eight — it had found one screen where there are two.

psc-top     0,0    ->  1280 x 31
psc-left    0,0    ->  31 x 720
psc-right   1249,0 ->  31 x 720
psc-bottom  0,689  ->  1280 x 31

It is not a scaling bug

That was the first guess, and it was wrong. SetProcessDpiAwareness(2) succeeds cleanly - HRESULT 0 - and the process reads 3840x2160 at 150% afterwards. Measured before assuming.

1280x720 is what Windows hands back to a process that asks about the desktop before the desktop is ready. The overlay asked exactly once, in its first line, and kept the answer for the rest of the session. It is started with the first block after the app launches, which on a cold boot is precisely when the answer is not ready yet.

The reading is no longer kept

It is taken again whenever the glow is about to be shown, and once a second for as long as it is up. That costs microseconds, and it covers every way screens can change without having to enumerate them: waking from sleep, a monitor switched on, resolution or scaling changed, a cable moved, a remote session - and the case above, where nothing changed at all and the first answer was simply wrong. Bars are created and retired to match.

On the message-loop thread, not the reader thread. Creating a window from the reader is the same mistake that hid the input hold for two releases; warn(), lock() and wait_on() only stamp a clock now, and tick() does the work.

And the eye is no longer the only witness

The overlay reports the rectangles it draws on, and self_test holds them against the screens Windows describes right now:

Is the warning drawn around your actual screens?   OK   3840x2160+0+0;1080x1920+3840+0

A glow in the wrong place is a line of text now, not something you have to happen to notice.

Third time for this shape

1.6.0 and 1.6.1 were about a call whose failure was swallowed. This one is about an answer that was correct for nobody and was never asked again. Same core both times: something asserts a state, and nothing checks whether it still holds.

New test file tests/test_monitors.py - nineteen now, green on Windows against Python 3.9, 3.11 and 3.13.

Install

Claude Desktop - pc-screen-control.mcpb -> Settings -> Extensions -> Advanced -> Install extension. Remove the previous version first and quit Claude completely, tray icon included.

ChatGPT desktop, Codex, Cursor, VS Code, Cline, Zed - pc-screen-control-gpt.zip -> extract -> INSTALL-FOR-GPT.bat -> restart.

Windows and Python 3.9+ with "Add python.exe to PATH". Run self_test - it should report 1.6.2.

Checksums

pc-screen-control.mcpb      8239855 bytes
  SHA-256 48db4afd1a8fcd9ee7506eb14565ae49acc9c5339f1c14ca06fe31867b106aef
pc-screen-control-gpt.zip   8140363 bytes
  SHA-256 92152d34c8f0283f0c75bab579b161f1427c18b94f9796ca6e3611aa434f4e43

v1.6.1 — three more of the same, found by looking

Choose a tag to compare

@desteny-dev desteny-dev released this 04 Aug 13:04

1.6.0 fixed a SetFocus whose failure was swallowed. That is a pattern, not an incident — so the rest of the file was audited for it: every call whose result is discarded and whose success the next line assumes. Three more turned up.

focus_window reported ok: True without checking

It used a bare el.SetActive(), which Windows refuses silently for a background process — exactly like SetForegroundWindow. The robust version, with the thread attachment and the foreground-lock timeout, already existed in this file as _vordergrund_setzen. But only the restore path used it. The tool a caller actually reaches for had the naive one.

That is how an assistant ends up believing it is in a terminal when it is not: it called focus_window, got ok: True, and typed.

It now verifies, reports in_front, returns ok: False with what to do instead — and does not declare a target it could not reach, because a declared target that is not in front is exactly what blind typing trusts.

close_window's Alt+F4 fallback sent regardless

Same unverified call, then Alt+F4 — which closes whatever is in front. It verifies first now and sends nothing if the wrong window is there.

capture could caption a picture "window: X" while showing what covered X

It says so in the caption now.

Checked and deliberately not changed: menu waits for the popup to appear, window returns before/after state. Both already verify their own effect.

Versioning, corrected

1.3.0 → 1.6.0 happened in two days, all of it repairs. The rule that caused it was "any new refusal is a minor" — wrong for this project. A guard that becomes stricter to close a hole it should always have covered is a fix, not a feature.

From here: tightened guards are patches; minors are new tools, arguments or reply fields. 2.0 will be reached on capability, not by accumulating repairs.

Install

Claude Desktoppc-screen-control.mcpb → Settings → Extensions → Advanced → Install extension. Remove the previous version first and quit Claude completely, tray icon included.

ChatGPT desktop, Codex, Cursor, VS Code, Cline, Zedpc-screen-control-gpt.zip → extract → INSTALL-FOR-GPT.bat → restart.

Windows and Python 3.9+ with "Add python.exe to PATH". Run self_test — it should report 1.6.1.

Checksums

pc-screen-control.mcpb      7772400 bytes
  SHA-256 411004b6060db8311f15a50bd6ceb824a216e86e0a7060148d6048771b2f2a6b
pc-screen-control-gpt.zip   7678559 bytes
  SHA-256 23bc7eea904d1e6530228fe8860de4e2efebd8f687e49f63cac5ae2473a9a4d8

v1.5.0 — parked windows no longer take the screen

Choose a tag to compare

@desteny-dev desteny-dev released this 03 Aug 13:39

Parked windows no longer take the screen.

claim_window moves a window past the edge of every monitor: it stays fully operable by name, but it is not visible and the pointer cannot reach it. Operating one therefore changes nothing you can see. Until now it still held your keyboard while doing so.

invoke, set_text, toggle, expand, select and set_value on a claimed window now open no block — no pulse, no notification, no held input.

Still guarded: click, drag, send_keys, hold_key (they act wherever the hardware points, not on a window), and anything that changes what is visible.

Also in this release

  • README rewritten and halved (339 → 179 lines). New section: which tools use your screen and which do not — 30 of 34 never touch the mouse or keyboard.
  • macOS is stated as not available. docs/PORTING.md is a map, not an implementation.
  • pytest -q works. It previously aborted collection with exit code 0, which looked like "no tests exist".

Install

Claude Desktop: pc-screen-control.mcpb → Settings → Extensions → Advanced → Install extension. Remove the previous version first and quit Claude completely, including the tray icon.

ChatGPT desktop, Codex, Cursor, VS Code, Cline, Zed: pc-screen-control-gpt.zip → extract → INSTALL-FOR-GPT.bat → restart.

Requires Windows and Python 3.9+ with "Add python.exe to PATH". Run self_test afterwards; it should report 1.5.0.

Checksums

pc-screen-control.mcpb      7769823 bytes
  SHA-256 ba6de10b9812b9281bf97c94892c5331c67ae80f1cc2180ed6da5848658b57e0
pc-screen-control-gpt.zip   7676078 bytes
  SHA-256 9b8a160a06d3733716b04ffce97714b491e1c15c0e1f68d3f3a38baab6676e36

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

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

v1.4.1 — the stop button did not work while the screen was held

Choose a tag to compare

@desteny-dev desteny-dev released this 01 Aug 21:58

The stop button did not work while the screen was held

Install this one. The tray menu is the only way to pause or stop a takeover from outside. The code said a takeover could not lock anyone out of it, and gave a reason:

the tray icon and its menu are a normal window, not part of the swallowed input.

That reasoning is wrong. A low-level mouse hook intercepts input before any window sees it — being a normal window has nothing to do with it. So while a block was held, a real click on the tray icon was swallowed like every other click, and Pause and Stop were unreachable during the only moments they exist for.

The emergency brake did not work while the car was moving.

What changed

The taskbar is carved out of the swallowing, and so is the whole screen while the menu is open — keyboard included, so the menu can be driven with arrow keys and Enter. TrackPopupMenu runs its own message loop and does not return until the menu closes, so the flag is set around it in a try/finally. The taskbar's rectangle is re-read once a second, because it can move or auto-hide.

The risk of the carve-out is small and worth it by a wide margin: the taskbar is not where the assistant works, and somebody reaching for the tray during a takeover is trying to stop it.

How it was found

Not by anything going wrong — by working through the last item on the open list, which had said for months that the tray controls "must stay clickable while the guard is swallowing the mouse, or they are decoration." They were decoration.

What is still unmeasured, and now the only thing left on that item: a human clicking it mid-takeover and seeing the menu open. If you install this, that is the one thing worth trying.


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.1, 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: 43e240de2fc6fa320508c9bca5513c789206cecbfdc6a1cf7f7d0fddce30926e
  Size:    7767974 bytes

pc-screen-control-gpt.zip
  SHA-256: 112df4197be5e6f901647b1773914478397c92e1b00f0d2702923f127a1e6c0e
  Size:    7674290 bytes

v1.4.0 — the visual half, measured for the first time

Choose a tag to compare

@desteny-dev desteny-dev released this 01 Aug 19:14

Why this is 1.4.0 and not 1.3.5

1.3.1 through 1.3.4 all shipped in a day, and at least two of them changed what the server refuses. New refusals are a change in behaviour, not a patch. Calling them patch releases was wrong, and it made a manual install cost more than the fix was worth.

From here: patch = fixes only. minor = any new refusal, tool, or reply field the caller must act on. And not more than one release a day unless something is actually costing somebody work.


The visual half had never been measured

The pulse, the notification and the tray icon were the two oldest items on the open list — written, shipped, never checked on a real desktop. Checking them found three defects, none of them in the drawing.

The guard could go missing and never come back

_overlay_starten returned the stored handle whenever it was not None — which is true of a dead process too. So the first time the overlay ended for any reason, every later command went into a pipe nobody was reading.

The overlay is not decoration. The warning, the pulse, the notification and the input hold all live in it. Losing it once meant losing the guard for the rest of the server's life, silently. It is restarted now — with a cooldown, because "restart it" and "restart it on every call" are not the same thing — and if it will not stay up, self_test says so instead of the server pretending.

A leftover overlay sat glowing with nothing held

Four bars visible, nothing holding, from an earlier server. A glow that means nothing is worse than no glow, because the next real one means nothing either. Anything on screen is now taken down whenever the state is "off", whatever path led there.

The pulse framed the desktop, not your screen

The bars followed the virtual desktop — one box around all monitors. Measured here on two screens of different height: the bottom edge of the desktop sat 240px below the smaller monitor, so somebody working on that screen got a top edge, a right edge, and nothing else. Half a frame reads as a glitch, not a warning.

Each monitor now gets its own complete frame. The held bar is also re-drawn a few times a second and re-asserted as topmost — being topmost once is not being topmost, since any window that asks later goes above. The pixels are cached, so the redraw costs less than one frame of the animation before it.


What the measurement confirmed

Pulse red #EF4444 reaching ~200px inward at 800ms, snapping to blue #22D3EE at 46px — read as screen pixels on all four edges of both monitors, before, during and after
Notification appears
Input hold the overlay reports hooks:1 on lock, hooks:0 on release

self_test now reports guard_overlay: is the process running, how often did it have to be restarted, has it been given up on, what did it last say about the hooks.


Two more open items closed

wait inside batch is refused past 2 seconds. The block is held for the whole batch, so a ten-second wait is ten seconds of somebody locked out of their own screen while nothing happens. It was written down as a rule, and rules that are only written down get broken. Short settles still pass — a window needs a beat to catch up.

_spur_suchen measures itself. self_test reports stale_ref_rescue: runs, nodes walked, seconds, worst single run, and how often the 4000-node limit was hit. Whether that walk should become an index is now a question for numbers rather than for a feeling that 4000 sounds like a lot.


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.0, 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: 97aecf13d44ea192eef2e1be40f8baf3774ae63d1840e65b4c7d9fa8329c3de3
  Size:    7767044 bytes

pc-screen-control-gpt.zip
  SHA-256: 46af901293ca13b006734bfcb2061e080b41b15a59c292c5a4f908a0fa71a26d
  Size:    7673387 bytes

v1.3.4 — telling the caller what it cannot remember

Choose a tag to compare

@desteny-dev desteny-dev released this 01 Aug 17:22

The reader of these replies has no memory of the machine

Install this one — it includes 1.3.3.

An assistant calling this server does not remember the desktop between turns. It knows what the last reply told it, and nothing else. Two things it could not know now travel with every acting call.

Is the block still open

Only set_guard reported that. After two turns the assistant is guessing — and a block held open by a forgotten start is exactly how somebody ends up locked out of their own desk.

Every reply from a tool that can change the screen now carries:

{
  "block_open": true,
  "seconds_held": 12.4,
  "input_held": true,
  "working_in": "Notepad",
  "reminder": "End the block with set_guard block:'end' the moment you no longer need the screen."
}

input_held is the measured one — when Windows refuses the input hooks it says so, and adds that this is a shared screen and not to type without a ref. Readers are not nagged; they open no block.

What was swallowed during this call

_safe hides exceptions on purpose: one control that refuses to answer must not abort a walk over two hundred of them. Every swallow was already recorded — but only self_test showed them, and nobody runs self_test mid-task. So a call that quietly lost three exceptions looked exactly like a clean one.

Anything swallowed during this call is now attached to the result it may have shaped, with the type, the line, and what it means for that result. A clean call says nothing.


Prompted by a review arguing that with an LLM as the user, the guard is no longer politeness but an error-detection system. That framing is right, and these were the two places the system could not see itself.

The same review's other headline items were checked against the code and are not open: the ring buffer it asks for is _FEHLER_LOG and shipped before 1.0.0, _safe has never been except: pass, and there is no update download to verify a hash of — this server opens no socket.

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.3.4, 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: 8b147e25e0eb425dd61612a7b0c007e76b7eb1a148fab20ea6915b7241b44c11
  Size:    7763253 bytes

pc-screen-control-gpt.zip
  SHA-256: 0d0936a022dd3ab9fa5b50d8e3aa37ebc08bfb2bd35868fd09733a81f772748c
  Size:    7669630 bytes

v1.3.3 — the input hold was never actually on

Choose a tag to compare

@desteny-dev desteny-dev released this 01 Aug 17:02

The input hold was never actually on

Install this one. Reported in one sentence, and it undoes an assumption the whole design rested on:

"I could still move my mouse and type while you had my window."

A low-level hook is delivered to the message queue of the thread that installed it. The commands from the server arrive on the overlay's stdin thread — and that thread sits blocked in a read, with no message loop at all. So SetWindowsHookEx succeeded, returned a valid handle, and the callbacks were never dispatched. Windows drops such a hook after LowLevelHooksTimeout and says nothing.

Nothing failed. Nothing was logged. self_test reported the overlay as present, because it checked that the file existed. The pulse drew, the notification appeared, the block opened and closed — and the person's keyboard and mouse were free the entire time, while every layer above was certain they were held.

The fix is one line of architecture

The protocol thread now only records a wish; tick() — which runs on the message-loop thread — installs and removes the hooks. That is the one thread that can receive the callbacks, so it is the only correct place.

And it now says so. The overlay reports hooks:1 / hooks:0 back to the server, and set_guard block:'start' returns input_held. When Windows refuses the hooks, the reply says the screen is shared and to behave accordingly — instead of promising a hold that is not there.

Ending a block now tells you what came back

Found in the same live test: the release wrote its result into _RUECKGABE on every block, with a comment saying tools copy it into their reply. Nothing did. The claim "your focus is restored is a measurement, not a promise" was true of the measurement and false of the reporting.

set_guard block:'end' now returns handed_back:

field
foreground_restored did the call succeed
their_window the window this block owed them
in_front_now what is actually in front, measured after the restore
caret_restored did the text cursor come back with it
attempts how many tries it took

in_front_now is the one that matters. foreground_restored answers whether the call worked; in_front_now answers where the person is looking.

Third defect in this project found by the same rule: a function that does not report whether it worked will eventually stop working, and nobody will notice.

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.3.3, 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: 4c55954cfd2afa0393cab70f9404ed9bfb4c83b5a6d48e1bdab391101473272e
  Size:    7762404 bytes

pc-screen-control-gpt.zip
  SHA-256: 4bfe93bea4eb392e690b4487687b375f9fcb996954b3c5e3d096c89ec3cc0655
  Size:    7668773 bytes

v1.3.2 — your window is not ours to make disappear

Choose a tag to compare

@desteny-dev desteny-dev released this 01 Aug 15:56

The window you were in is not ours to make disappear

Reported from real use: "when it takes control my active window goes to the background, and now and then it was even closed."

Three tools can make a window vanish — close_window closes it, claim_window moves it past every monitor, window state:minimized hides it — and none of them knew which window the person was sitting in. A window_title that matched theirs instead of ours was the whole distance between working and lost work. A fourth path was not a tool at all: Alt+F4 or Ctrl+W sent without a ref, landing on whatever happened to hold the keyboard.

The takeover already saves that window, because it has to put it back at the end. The same handle is now used for the opposite purpose: it is the one window those tools refuse to touch.

Refuse rather than warn — a warning arrives after the window is gone.

now
Closing, parking or minimising your window refused. Naming that exact window_handle and confirm:true still gets through, so "close my Notepad" works. What is closed off is the accidental path
An ambiguous window_title before something destructive refused, not guessed — three windows containing "Chrome" come back as three candidates with their handles
Alt+F4 / Ctrl+W without a ref refused. Every other stray keystroke can be deleted again; these cannot
Your window did not come back to the front said out loud on the next call, with the handle, the title and what to do
Watch mode explains itself once. It skips the restore on purpose; somebody who switched it on last week only sees their window ending up behind

The restore also gets three attempts now instead of two. A block often ends on a timer, with no reply to attach a failure to — which is how a foreground that never came back stayed invisible.

tests/test_user_window.py replays all four paths, and just as importantly the cases where nothing may be refused: another window, a named handle with confirm, outside a block. A guard that blocks correct work gets switched off, and then it is not there on the day it matters.

This is enforced by the extension, in every chat, with every client — not by instructions and not by the person.

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.3.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: c961cd105f79c5ef7df004fa2973752066f169f9e688f9072e1d1fa0b842e1c8
  Size:    7760433 bytes

pc-screen-control-gpt.zip
  SHA-256: 41ae77de72b83b35ef44a6bf1a97554e638398e1f39dabb56b396c42a1fe510e
  Size:    7666876 bytes