A Mac your agent is allowed to destroy

The safest place for an agent to do risky work is a Mac you can throw away. macos-mcp vm clones a golden macOS image, starts the clone in a window on this Mac, lets the agent wreck it, and deletes it. Shipped for a single host in v0.2.0 on 2026-09-07; the multi-host hub is in development.

Native Virtualization.framework2 macOS guests per host1 click per golden image0 clicks per cloneCLI subcommands, not MCP tools
Disposable Macs for agents

Clone, start, wreck, destroy.

macos-mcp vm clone golden-27 scratch    # APFS clonefile, fresh machine identifier
macos-mcp vm start scratch              # a window in this Mac's console session
macos-mcp vm verify scratch             # drivable from inside, with no human in it
macos-mcp vm ip scratch                 # the guest's NAT address, resolved by MAC

#  … the agent installs the untrusted thing, runs the script it was given,
#     clicks the button nobody wants to click on their own Mac …

macos-mcp vm screenshot scratch --output /tmp/scratch.png
macos-mcp vm destroy scratch            # gone

The rest of the group is list, stop, status, grant and input; every one takes --json, because the caller is usually a script.

Bundles live in a store — ~/.macos-mcp/vms by default — images and their clones side by side. Cloning a running image is refused, because copying a live disk copies an inconsistent one, and destroy refuses an image unless forced, so the machine you spent a click on survives a typo. A running guest is a window on your screen: you can watch the agent work, and take the mouse back.

Cloning

Why a fresh Mac costs nothing.

A clone is an APFS clonefile of the bundle — instant, and no disk at all until the guest writes. Cloning a macOS VM with APFS is a metadata operation, so a fresh machine is not an install; it is a copy that copies nothing.

What must never be shared between two guests is regenerated rather than copied: a fresh VZMacMachineIdentifier, the clone's own MAC address, its own auxiliary storage. The identifier is written and then read back, and a clone whose identity did not actually change fails with a typed action_unverified instead of booting as a twin of the image it came from. Read back, never assumed.

The permission problem

One click per image. None per clone.

A macOS guest cannot be driven from outside. Host-synthesized events never reach it; its own screen sharing authenticates and then serves black while discarding every input event; its TCC database is protected by SIP; a hand-written PPPC profile grants nothing without a supervising MDM. Every outside-in route measured on the testbed was ruled out.

So the grants that let something drive the guest from inside — Accessibility and Screen Recording, for the console user's ssh sessions — cost one human click on a Privacy pane. macos-mcp spends it once per golden image and never again:

macos-mcp vm grant golden-27 --seal     # the one physical click, then sealed for cloning
macos-mcp vm verify scratch --destroy   # per-clone acceptance, then throw the clone away

vm grant boots the image, provokes the entries so they are listed, opens the pane, prints exactly what to click, probes from fresh ssh sessions until both grants read back, records them in the image's manifest.json, and with --seal shuts the image down so it is at rest for cloning. Every clone inherits the grants.

vm verify is the acceptance test for a clone: with no human inside the guest, an ssh session as the console user reads the accessibility tree, posts a keystroke and takes a screencapture, while the host confirms the VM window is not a black rectangle. It exits nonzero when any of that fails, so “this clone is drivable” is a check in your pipeline rather than a hope.

The password dialog stays yours. vm grant --drive does the mechanical part — dismiss the update modal, open the Privacy pane, add the grantee — reading the guest host-side and clicking through the runner's own view — then stops at the macOS password prompt and hands off, because automating a system authentication dialog is a hard line. On an operator's own disposable image, --drive --password is an opt-in that finishes both panes hands-free and verifies against the guest's own TCC database before sealing.

Measured, not assumed

The limits are the design.

capacity

Two guests per host, and that is a kernel quota

Measured on macOS 27 beta 4: the third macOS VM is refused instantly with VZErrorDomain code 6 while 39% of host RAM is still free. Bigger hardware cannot raise it, so macos-mcp refuses the over-quota start up front with a typed error and a scheduler routes elsewhere instead of retrying into a wall. Fleet capacity is hosts × 2.

display

A guest renders nothing on its own

A VZ guest produces an all-zero framebuffer unless a view is attached in a logged-in session, so a running VM is a window in the console user's session on the host. A locked host blacks out every VM on it, so a Mac that hosts guests never locks and never sleeps.

capture

Screenshots read the host's view

A guest's own screen sharing serves black even when the guest is healthy and capturing itself fine, so it is never the health signal. vm screenshot captures the VM window host-side and reports what fraction of it is not black — process health means nothing here; pixels do.

Not shipped: the hub. Everything above is a single host, today. The multi-host layer — a machine registry, enrollment, leases, the machine parameter that routes any tool to any Mac, display streams and the orchestrator API — is designed, prototyped and measured, and it is in development. No tool takes a machine argument yet. Fleets and VMs: the whole design →

How it fits

Same binary, same rules.

FAQ

Questions people ask about running an AI agent in a macOS VM.

Can I run more than two macOS VMs on one Mac?

No. Two macOS guests per Apple Silicon host is a kernel quota, not a resource limit: measured on macOS 27 beta 4, the third guest is refused instantly with VZErrorDomain code 6 while 39% of host RAM is still free. More RAM never raises it, so capacity is hosts times two and multi-host scheduling is the answer. macos-mcp refuses the over-quota start up front with a typed error rather than letting the guest layer fail opaquely.

Does the VM need its own Apple Account?

No. A golden image is built from an IPSW and pre-suppresses Setup Assistant, the per-user first-login wizard and the Apple Account pane, so a clone boots straight to a usable desktop. Nothing in clone, start, verify or destroy signs in to an Apple Account. The one thing a guest does need is the console user's ssh key and the two Privacy grants, and those are solved once per image.

Is this Docker for Macs?

No. These are full macOS guests on Apple's Virtualization.framework, not containers. There is no shared kernel, no layered image format and no registry: a VM is a bundle in a store, a clone is an APFS clonefile of that bundle, and a running guest is a window in the host's console session rather than a background process. The container-shaped part is the lifecycle - clone, use, destroy - not the isolation mechanism.

Do I need to click anything?

Once per golden image, never per clone. An ungranted guest has no input channel from outside, so the Accessibility and Screen Recording grants its console user needs are one human click on a Privacy pane inside the guest. macos-mcp vm grant does everything around that click and records the result in the image manifest; every clone inherits it, and macos-mcp vm verify proves a clone is drivable with no human in it. On your own disposable image, vm grant --drive --password will finish both panes hands-free, but that is opt-in: by default the tooling stops at the macOS password dialog and hands off, because automating a system authentication dialog is a hard line.

Where does the golden image come from?

You build it once from an IPSW using the documented recipe in the repository's knowledge/ directory, which covers provisioning, auto-login, the first-login wizard keys read back off a live guest, and ssh. macos-mcp vm takes over from there: it clones, starts, stops, captures, grants, verifies and destroys the bundles in its store. Image building is not a subcommand yet.

Can my agent call these as MCP tools?

Not today. macos-mcp vm is a CLI subcommand group, so the frozen tool surface is unchanged at 44 tools, 24 on by default. An agent drives VMs by running the CLI, or by talking to a macos-mcp running inside the guest. The machine parameter that routes any tool to any Mac belongs to the hub, which is in development.

Give your agent a Mac it can break.

$curl -fsSL https://macos-mcp.sh | sh

Apple Silicon, macOS 15 or later; the VM subcommands are in v0.2.0 (macos-mcp vm --help). The golden-image recipe and every measurement behind this page are in the repository's knowledge/ directory.