computermcp

Security model

What this defends against, how each gate actually works, and - the part most pages skip - what it does not protect you from.

The threat we built for

An agent with a screenshot tool and a keyboard on a working developer's Mac. Not a hostile user: a useful agent, on a machine that happens to also hold an SSH key, a password manager, a customer database and a production terminal. The failure modes are mundane and they are why people don't run these tools where the work is:

FailureWhat Computer MCP does
A screenshot catches a password field mid-type, or a vault while unlocked.Secure fields and password-manager windows are painted out in memory, before the file is written.
An injected instruction on a web page becomes a keystroke in a terminal.Terminals are on the always-ask list. Every attempt is logged whether it is allowed or refused.
An agent does something irreversible while nobody is watching.Writes need consent. An unanswered dialog is a refusal, not a default yes.
Nobody can say afterwards what the agent actually did.Append-only log of every call, its target, and the decision.

Gate 1 - redaction happens before the file exists

The order is the whole point:

capture (in memory)
  → find secure rectangles via the Accessibility API
  → paint them opaque black on the bitmap
  → only then encode and write the PNG

Had we written the file first and redacted after, there would be a window - short, but real - where an unredacted picture of your desktop sat on disk for any other process to read. A security property with a race in it is not a security property.

Two things get painted:

The rectangles are painted solid black, not blurred or pixelated. A good enough model can read through pixelation. It cannot read through black.

How we know it works. test/redaction-unit.py builds a synthetic image with a marked block, redacts it, and checks four things: the block is black, the background is untouched, redacting somewhere else leaves the block intact, and a block in the top half is not mirrored to the bottom. That last one catches a flipped y-axis - the bug that would silently black out the wrong part of your screen while leaving the password visible. We verified the test by breaking the code on purpose: with the flip removed, it goes red and names the reason.

Gate 2 - consent, and what it is not

Three modes. ask is the default.

readonlyWrite tools are not listed at all. A model that can see a tool will eventually try it; in read-only mode the hands do not exist.
askThe first write opens a macOS dialog naming the specific action. Approving grants the rest of the session.
allowWrites proceed, still logged.

Two rules override all three:

  1. Always-ask apps ask every time - password managers and terminals - including in allow mode and including after the session was approved. This is not configurable. The difference between “I let the agent work” and “I let the agent into my vault” is worth one dialog.
  2. Silence is refusal. The dialog expires and the call is denied. This is the property most likely to fail quietly, so it has its own test, and that test has been mutation-checked: make the timeout resolve to yes, and the test catches a click that should never have happened.

Consent is not containment. After you approve a session, the agent is driving your actual Mac - that is what you approved. Computer MCP makes the dangerous moments visible and deliberate. It does not make them impossible. If what you need is a boundary rather than a decision, run the agent in a VM, and treat that as the honest answer rather than a feature we are missing.

Gate 3 - the audit log

~/.local/state/computer-mcp/audit.jsonl, mode 0600, appended only. One line per call: the tool, the tier, the scrubbed arguments, the app it targeted, the mode, and whether it was allowed or refused with the reason.

Typed text is never stored in clear. It is recorded as a length and the first twelve hex characters of a salted SHA-256:

{"ts":"2026-09-18T03:01:37.485Z","tool":"computer_type","tier":"write",
 "args":{"text":{"length":19,"sha256_12":"9f2b41c0d7ae","salted":true}},
 "target":"com.apple.TextEdit","mode":"ask","decision":"allowed"}

That is enough to prove two actions typed the same thing, and never enough to read what it was. A log that captured every keystroke would be the first file worth stealing on the machine - and it would undo gate 1 in a different format.

Ask your agent to call computer_audit and it will read the log back to you.

What is deliberately not here

There is no tool to run a shell command, open an arbitrary file, or fetch a URL. Those would each be one line of code and they are all absent on purpose: a computer-control server with a shell inside it is remote access with a friendlier name. If you want a shell, use a shell MCP server - then you have chosen it, and the choice is visible in your config.

There is no model inside this server and no API key. The agent you already run does the seeing and deciding. Nothing is sent to us, because there is no service of ours in the path.

This website is held to the same rule: no analytics, no cookies, no third-party requests. The only script tag on the site is structured data for search engines, which the browser does not run. Search-engine verification, where we use it, lives in DNS rather than on the page.

The helper binary that receives Accessibility and Screen Recording permission has zero third-party dependencies. Every package in a binary that privileged is another vendor you are trusting without having chosen to.

Known limits

Reporting something

Security reports: hello@agent360.dk, or a private advisory on the GitHub repository. If you find a way past a gate described on this page, that is exactly the thing we want to hear about - please say so before it is public, and we will credit you when it is fixed.