Skip to content

Releases: kanishka089/computer-use-mcp

v0.3.2 - upgrade docs on the PyPI page

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 10:53

No code changes — 0.3.1's behaviour is unchanged.

PyPI renders the README as the project description and only refreshes it on a new release, so the new Upgrading section wasn't reaching the people most likely to need it: anyone landing on pypi.org/project/realhands looking for how to move off an old version.

If you are on 0.1.x, please upgrade

Those releases import an API that mcp 2.0 removed. An install made back when it resolved mcp 1.x still runs, but any reinstall — or moving to another machine — now produces a server that cannot start. 0.2.1 and later handle both majors.

Recommended, so you never do this again:

claude mcp remove realhands --scope user
claude mcp add realhands --scope user -- uvx realhands@latest

Staying on pip? Upgrade the interpreter the server actually runs, not whatever pip resolves — run claude mcp get realhands, read the Command: line, and use that Python explicitly:

& "C:\path\from\that\Command\python.exe" -m pip install --upgrade realhands

A bare pip install --upgrade realhands will happily upgrade a different environment and leave the server on its old version. That is the most common reason an upgrade appears not to take.

v0.3.1 - all three channels publish themselves

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 09:45

Release automation is now complete: a single gh release create publishes to GitHub, PyPI, and the MCP Registry, with no credential to mint, expire, or store anywhere.

PyPI has published itself since 0.2.0 via Trusted Publishing. The registry step was still manual — mcp-publisher login github mints a device-flow token that lives about an hour, so every release meant repeating a browser device-code dance. It expired mid-release twice while shipping 0.2.x.

The new registry job uses mcp-publisher login github-oidc, exchanging the workflow's OIDC token for a registry JWT. The repository owner must match the io.github.<owner>/* namespace in server.json. Same trick as PyPI Trusted Publishing, same benefit: nothing to leak.

It runs after PyPI rather than beside it — the registry entry points at the published package, so listing a version PyPI doesn't have yet would leave a broken entry.

Also guarded

server.json carries the version in two places (top-level and packages[].version). A release where those disagreed is the likely cause of the phantom "duplicate" that blocked the 0.1.1 registry publish months ago. CI now fails the release if either disagrees with the tag.

No functional changes to the server itself — 0.3.0's mcp 1.x/2.x support and 0.2.0's batching are unchanged.

v0.3.0 - works on mcp 1.x and 2.x

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 09:40

0.2.1 pinned mcp<2 to stop fresh installs dying at import. That was a holding action — it left you stranded on a superseded major. This ports the server properly.

Both majors, no pin

mcp 2.0 renamed FastMCP to MCPServer and moved it to mcp.server.mcpserver. Everything realhands uses is otherwise identical across the two: the .tool() decorator, .run() defaulting to stdio, Image(data=, format=), and a tool returning [str, Image] producing TextContent + ImageContent. So rather than move the pin, it imports either:

try:      from mcp.server.mcpserver import MCPServer as _Server, Image   # mcp >= 2.0
except:   from mcp.server.fastmcp   import FastMCP   as _Server, Image   # mcp 1.x

The dependency is back to an unbounded mcp>=1.2. Verified against mcp 1.29.1 and 2.1.0 — the server imports and registers computer with an identical 9-parameter schema on both.

CI now proves this instead of assuming it

The smoke test is its own job running on windows-latest — the platform this package targets, where pyautogui and pywin32 can actually load — across a matrix of both mcp majors. It imports realhands.server and asserts the tool still exposes action, steps, screenshot, coordinate and monitor. Silently losing the batching parameters would be a worse regression than a hard import error, and only a contract check catches that. Publishing depends on it passing.

Also

GitHub Actions moved off Node 20, which the runners deprecated: checkout v4→v7.0.1, setup-python v5→v7.0.0, upload-artifact v4→v7.0.1, download-artifact v4→v8.0.1 — all repinned to full commit SHAs, since the publish job holds id-token: write.

Upgrade: uvx realhands@latest picks this up on your next client restart. On a pinned install, pip install --upgrade realhands.

v0.2.2 - batches abort when the target window is missing

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 09:28

A safety fix for steps batching, introduced in 0.2.0.

activate_window used to return No window matching '<title>' as an ordinary status string. In a single-action call that was harmless. In a batch it was not: that step decides which application the following keystrokes land in, so reporting the miss and carrying on typed the rest of the batch into whatever happened to have focus.

This is not hypothetical — it happened while driving a release. Chrome had been closed, activate_window reported the miss, and the batch went on to type a URL and press Enter into the editor that had focus. On a tool that drives the real keyboard, a step that silently retargets the rest of the sequence is a hazard, not a warning.

activate_window now raises instead, still listing the open window titles so the failure stays diagnosable. The batch runner already stops on the first exception and reports which step failed, so the run aborts at the right point and no later step executes:

STEP 1 (activate_window) FAILED: LookupError: No window matching 'Chrome' —
nothing was focused, so no further steps were run. Open titles: [...]

Single-action activate_window calls now surface a tool error rather than a success message with a "no match" note — the same information, correctly classified.

Upgrade: uvx realhands@latest picks this up on your next client restart. On a pinned install, pip install --upgrade realhands.

v0.2.1 - fixes broken installs

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 09:20

If you installed realhands 0.1.1 or 0.2.0 and it failed to start, this is the fix. Please upgrade.

The bug

mcp 2.0 removed mcp.server.fastmcp (FastMCP moved out of the SDK). realhands declared its dependency as an unbounded mcp>=1.2, so any fresh install resolved to mcp 2.x and the server died immediately at import:

ModuleNotFoundError: No module named 'mcp.server.fastmcp'

This affected 0.1.1 and 0.2.0 alike. It went unnoticed because existing environments still had an mcp 1.x resolved from an earlier install — only a clean resolve exposed it.

0.2.1 constrains the dependency to mcp>=1.2,<2. A port to the mcp 2.x API will come separately.

Preventing a repeat

CI now installs the built wheel into a clean virtualenv and imports it before publishing. twine check validates packaging metadata only — it cannot catch a dependency resolution that yields an unimportable package, which is how a broken wheel shipped twice.

Recommended install now auto-upgrades

claude mcp add realhands --scope user -- uvx realhands@latest

uvx resolves the newest release every time the server starts, so future fixes reach you on your next client restart with nothing to run. The pinned pip install realhands route is still documented for offline or version-locked setups — that one needs pip install --upgrade realhands by hand.

Upgrading from 0.2.0

Everything in 0.2.0 — action batching via steps, patch-aligned screenshots, unchanged-screen suppression — is unchanged. This release only fixes the dependency.

v0.2.0 - token-efficient screenshots

Choose a tag to compare

@kanishka089 kanishka089 released this 25 Aug 08:11

Screenshots dominate the cost of driving a desktop, and not just once: every image stays in the conversation and is re-sent as history on every later turn. Claude bills vision in 28x28 patches (tokens = ceil(w/28) * ceil(h/28)), so this release attacks that directly.

Batch your steps

Pass steps - a list of action dicts - instead of one call per action, and the whole run shares one screenshot at the end:

{"steps": [{"action": "left_click", "coordinate": [420, 300]},
           {"action": "type",  "text": "hello@example.com"},
           {"action": "key",   "text": "Tab"},
           {"action": "type",  "text": "secret"},
           {"action": "key",   "text": "Return"}]}

That is 1,125 visual tokens instead of 5,625, and one round trip instead of five - the larger saving, since each avoided turn also avoids re-sending the entire transcript. A failing step stops the run, reports which step failed, and still returns the screen. Add "screenshot": false to skip the trailing image too.

Patch-aligned downscaling

A dimension that isn't a multiple of 28 pays for a partial patch row/column carrying almost no pixels. The default MAX_DIM moves 1280 -> 1260, so a 1920x1080 primary sends 1260x700 = exactly 45x25 patches = 1,125 tokens, versus 1,196 for 1280x720. 6% off for 1.5% fewer pixels. Each axis rounds independently, so to_real() now maps each with its own scale factor and stays exact.

Unchanged-screen suppression

If under COMPUTER_USE_CHANGE_THRESHOLD of pixels moved since the last image sent, the reply is a line of text instead of a screenshot. A real desktop never produces two byte-identical frames (clock, caret, hover states all jitter ~0.5-1% of pixels), so this is a threshold, not an equality check. After COMPUTER_USE_MAX_SKIPS suppressions in a row it force-sends one, so the model can't fly blind if it lost the earlier image to context compaction.

Also

  • Tool description trimmed from ~833 to ~705 tokens (charged on every request).
  • Documentation fix: .env.example previously claimed COMPUTER_USE_IMAGE_FORMAT=jpeg "cuts tokens further". It does not - cost is by pixel dimensions, not bytes. JPEG only cuts payload size (~977KB -> ~141KB), which helps latency at some risk to small-text legibility.

Keep the long edge <= 2576px: an image returned inside a tool_result is rejected rather than downscaled when it exceeds the model's limit.

Install: pip install --upgrade realhands

New config: COMPUTER_USE_PATCH_ALIGN, COMPUTER_USE_CHANGE_THRESHOLD, COMPUTER_USE_MAX_SKIPS. All existing calls keep working unchanged.

v0.1.0 — first public release

Choose a tag to compare

@kanishka089 kanishka089 released this 17 Jun 06:20

First public release of realhands — an MCP server that lets Claude operate your real desktop (physical mouse, keyboard, and screen) like a human.

Because it drives the actual OS cursor instead of a separate automated browser, it works in your own Chrome with your own logged-in sessions — and in any other app — undetectable as a bot.

Highlights

  • Single computer tool (action enum modeled on Anthropic's reference computer-use tool)
  • Pixel-accurate clicks under Windows display scaling (DPI awareness + stateless coordinate scaling)
  • Multi-monitor support (incl. negative-origin screens)
  • Three independent kill switches: fail-safe corner, panic hotkey, always-on-top STOP overlay (lazy arm / auto stand-down)
  • activate_window that beats Windows foreground-lock via AttachThreadInput

See the README for install + the full action list. Tested on Windows with Python 3.10/3.11.