macos-mcp · the ios toolset · 8 tools · off by default

Your agent can use your iPhone.

Not a simulator, not a jailbreak, not a patched daemon — the real handset in your pocket, through Apple's own iPhone Mirroring, driven from the Mac it is already mirrored to. Eight iphone_* tools read the phone's screen, find text on it, and tap, swipe, type and navigate in the phone's own coordinate space. Every action that touches the glass is read back, never assumed.

Requires iPhone Mirroring: iOS 18+, the same Apple Account on both devices, the iPhone locked and nearby. Apple does not offer it in the EU.

8 iphone_* toolsdevice points, not screen pixels0 false successesoff by defaultneeds iOS 18+not available in the EU
One task, end to end

Find it, tap it, read the result back.

iphone_status {}
→ state: "ready"
  phone.device_match: { quality: "matched" }

iphone_find { "text": "Calculator" }
→ matches[0].device_center: { x: 52, y: 604 }

iphone_tap { "x": 52, "y": 566 }   // the icon, above the label
→ confirmation_required
  token bound to this exact call (profile: standard)

iphone_tap { "x": 52, "y": 566, "confirm_token": "…" }
→ verification: { method: "frame_change",
                  verified: true, outcome: "changed" }

iphone_find { "text": "7" }
→ device_center: { x: 53, y: 676 }

iphone_tap { "x": 53, "y": 676, "confirm_token": "…" }
→ verification: { method: "frame_change", verified: true,
                  outcome: "changed",
                  changed_fraction: 0.0256 }

iphone_screenshot { "max_dimension": 1024 }
→ PNG cropped to the phone — the display reads 7

That last exchange is a true story. On 2026-09-08 the whole iphone_* surface was driven against a real iPhone through the shipping server, and Calculator's 7 key is where find-then-tap closed the loop: the tap landed, the screen moved, and a second iphone_find read a new 7 in the display.

The confirmation_required step is not decoration. Every iphone_* tool that moves the phone is annotated destructive, so under the default standard profile the first call returns a one-shot token bound to that exact tool and those exact arguments. An agent that wants to touch your phone says so twice, in the same words. The safety model →

One measured wart the transcript shows rather than hides: on the Home screen only an app's icon artwork is a hit target, and iphone_find returns the center of the text it read — the label. A tap on the label does nothing, verified four ways. Aim above it, or use iphone_key spotlight.

Coordinates

Device points, because screen pixels rot.

A screen pixel is an accident of this Mac, this display scale and where the Mirroring window happened to be when you looked; drag the window an inch and every coordinate in your transcript is wrong. macos-mcp never hands one out. iphone_find returns a device point — a coordinate in the phone's own logical space — and iphone_tap and iphone_swipe take the same, so a target found now survives a resize, a different Mac, and the same transcript replayed tomorrow.

Underneath are three coordinate spaces — window pixels, global screen points, device points — with the letterbox detected rather than guessed and one geometry shared by every tool, so a point read off iphone_screenshot cannot disagree with the point iphone_tap presses. The device profile behind the mapping is reported with its quality — matched, nearest or pinned — so a nearest guess never comes back looking like a fit.

Seeing

Finding text is OCR here — and that is a measurement, not a shortcut.

macos-mcp is accessibility-first everywhere else on the Mac: the tree first, pixels as the fallback. For a mirrored iPhone that ordering is not available, and it was measured rather than assumed — the mirrored content is an opaque video surface with no accessibility tree behind it. Nothing to snapshot, no element ids, no roles. So iphone_find (ScreenCaptureKit, Vision OCR, fuzzy matching) is the primary locator, and the tool descriptions say so rather than implying a snapshot they cannot deliver.

That inversion is half the story. The other half is what makes it trustworthy: the Mirroring app's own chrome does have a full accessibility tree, and that is what separates the states pixels cannot see. An alert over the phone still renders something phone-shaped; a screenshot would call it ready.

What is actually happeningiphone_status saysRead from
A live phone, ready to drivereadythe window's shape
The session paused itself after inactivityneeds_resumethe accessibility tree
The Mac's own lock screen over the phonelockedthe accessibility tree
Signed out — the window offers Sign Innot_connectedthe accessibility tree
A modal sheet is in front of the phoneblockedthe accessibility tree
Screen Recording was never grantedscreen_recording_missingthe grant, with its deep-link
Window is up but there is no phone in itno_phone_contentthe window's shape

A phone locked and far away, an unfinished setup screen, a phone in someone's hand and a region where Mirroring is unavailable look identical from outside the app: one state, no_phone_content, whose fix names all four. The projects that do separate them patch eligibilityd, which is a line this one holds.

No false successes

The phone has to change, or it did not happen.

Every mutating iphone_* call captures the phone before, acts, captures after, and reports whether the frame moved. A tap that changed nothing is a typed action_unverified error, never a green check. Proving iphone_key back on real glass looked like this: the edge swipe ran, 81% of the screen changed after 120 ms, and the screenshot showed Today View — what iOS itself opens for that gesture. Verified, not claimed.

The incident this rule is named after. An early build reported verified: true for a back gesture that never happened: the swipe started at device x = 1, which maps onto the window's resize border rather than the phone's screen edge, the Mirroring window shrank, and the frame-change check saw pixels move and called it a success. The verification had confirmed the wrong thing.

The fix came from a measurement, not a guess — a smaller inset still landed the press on the frame — so every posted point is now pushed 16 pt clear of the resize margin, with the adjustment reported in the body. The tool refuses to call a window resize an effect on the phone.

The detector underneath got the same treatment. It used to separate phone from window by brightness, which works until a dark app on a dark window leaves nothing to separate. It now reads the window's alpha shape: the window is cut to the phone's outline, and alpha does not care what the phone is showing. Verified on a Settings page that is almost entirely black — ready, aspect 2.1557, quality: matched, identical to what the same window reports on a bright Home screen.

iphone_type is the honest exception and says so on the wire: iOS hides the keyboard while Mirroring is active and the content has no tree, so nothing on this Mac can read the field back. It carries verification.method: "none" and points at iphone_screenshot. Branch on that, not on ok.

The honest limits

What this cannot do, stated up front.

Apple does not offer it in the EU

iPhone Mirroring is unavailable in the EU and Apple has repeatedly declined to ship it there. The ios toolset drives that app, so in the EU it has nothing to attach to — and nothing here works around the region check.

Mirroring's own preconditions

iOS 18 or later, the same Apple Account on both devices with two-factor on, Bluetooth and Wi-Fi, a passcode set, and the iPhone locked and physically near the Mac. Apple's terms, not ours.

Typing cannot be verified

iphone_type reports verification.method: "none": the keyboard is hidden and the field cannot be read back. It also carries Apple's known modifier-latching defect, which produces alternating case: mitigated by releasing every modifier first, not claimed as fixed.

It takes the keyboard

macOS routes synthetic input to the frontmost app only, so an iphone_* call holds the front for its duration: it cannot run alongside other ui_* automation on this Mac, cannot cross a Space, and will interrupt whoever is at the keyboard.

Off by default, and gated

44 tools, 24 on by default — none of the 24 an iphone_* tool. The ios toolset needs an explicit enable_toolset call, plus Screen Recording and Accessibility.

And one wart: iphone_key home on the Home screen's first page is a genuine no-op and returns action_unverified, because nothing on the phone changed — indistinguishable from a failed press, by design.

Getting started

Three lines and a mirrored phone.

$curl -fsSL https://macos-mcp.sh | sh
  1. Install, then macos-mcp install --client claude-code (or claude-desktop, cursor, vscode, codex). One signed binary, no runtime.
  2. Ask the agent to call enable_toolset ios, or start with --toolsets ui,screen,system,ios. Grant Screen Recording and Accessibility; macos-mcp doctor prints the System Settings deep-link for anything missing, and the client needs a restart after a grant.
  3. Open iPhone Mirroring so the phone is on screen, and ask: “Open Calculator on my iPhone and work out 7 × 8.”
FAQ

Questions people ask before enabling it.

Can Claude control my iPhone?

Yes, through Apple's own iPhone Mirroring. macos-mcp's ios toolset gives Claude Code, Claude Desktop, Cursor, VS Code or Codex eight tools that read the mirrored phone's screen, find text on it with OCR, and tap, swipe, type and press its navigation keys. It is not a simulator and not a jailbreak: it drives the real handset already mirrored to your Mac, in the phone's own device points, and checks that the screen actually changed before reporting success. The ios toolset is off by default — the agent has to call enable_toolset ios first, and macOS has to have granted Screen Recording and Accessibility.

Does this work in the EU?

No. Apple does not make iPhone Mirroring available in the EU, and macos-mcp drives the phone through that app, so there is nothing for the ios toolset to attach to there. The other 36 tools work normally. Nothing here patches or works around the region check — that is a line this project holds. If Apple ships iPhone Mirroring in the EU, this needs no code change.

Does it need a jailbreak or a developer certificate?

Neither, and no Developer Mode, no provisioning profile, no WebDriverAgent build, no patched daemon on the phone. macos-mcp captures the iPhone Mirroring window with ScreenCaptureKit and posts ordinary synthetic input into it, using the same two public macOS permissions a screen-sharing app asks for: Screen Recording and Accessibility. The phone is never modified and nothing is installed on it.

Can it read my messages or my 2FA codes?

It can read whatever is on the mirrored screen. That is what OCR means, and pretending otherwise would be dishonest. It is exactly why the ios toolset is off by default, why every tap, swipe, keypress and typed string is annotated destructive so that under the standard profile it first returns a one-shot confirmation token bound to that exact call, and why every call appends to ~/.macos-mcp/audit.jsonl with its arguments SHA-256 digested rather than stored. The hard lines still hold: secure text fields on the Mac are never read and never typed into, and macos-mcp never answers a macOS password or Touch ID prompt. If you would not want an agent seeing your phone, do not enable the toolset — and the default is that it is not enabled.

Does it work with the iOS Simulator?

No. The ios toolset drives a physical iPhone through iPhone Mirroring. Simulators are Xcode's territory — simctl and Xcode's own MCP server. macos-mcp's rule for Apple's servers is to mount them rather than reimplement them, and the Xcode mount is not advertised as working, because Xcode's consent dialog does not currently appear for third-party agent processes.

What happens if the phone locks or the session pauses?

You get a typed state instead of a wrong answer. iPhone Mirroring suspends the session after inactivity and shows a Resume button; iphone_status reports needs_resume, and iphone_resume presses it and then reports resumed or authentication_required rather than assuming either. A locked session draws a phone-shaped lock screen that passes every pixel test, so it is read from the window's own accessibility tree instead and reported as locked, with taps refused and the hand-off named. macos-mcp never presses Use Password and never types into what that opens — that is the Mac's own login.

Which iPhones and which versions does it need?

Whatever iPhone Mirroring itself needs: iOS 18 or later, the same Apple Account on the iPhone and the Mac with two-factor authentication, Bluetooth and Wi-Fi on, a passcode set, and the iPhone locked and physically near the Mac. On the Mac side macos-mcp needs macOS 15 or later on Apple Silicon. The device profile is inferred from the mirrored window's shape and reported with its quality — matched, nearest, or pinned when you pass one — so a nearest guess never comes back looking like a fit.

The iPhone is one toolset. The rest of the Mac is the other 36.

UI automation that reads back every action, screenshots and OCR, apps, files, Apple's own Safari MCP. Signed, notarized, audited. Give your AI agent a Mac →