Getting started
From curl to a working tool call in an MCP client in about three minutes. Apple Silicon, macOS 15 or later.
1. Install
curl -fsSL https://macos-mcp.sh | sh
The installer resolves the latest release, downloads the archive, verifies the SHA-256 checksum and the Apple Developer ID signature and notarization before trusting the binary (it fails closed on any mismatch), installs to /usr/local/bin/macos-mcp, and runs doctor so you see what to grant immediately. Read the script first if you like: it is what macos-mcp.sh serves, byte for byte from the repository.
Prefer to build from source? git clone https://github.com/Ark0N/macos-mcp && cd macos-mcp && swift build (Xcode 26 / Swift 6.1). The binary is then at .build/debug/macos-mcp.
2. Grant permissions
macos-mcp doctor
# Accessibility MISSING needed by: ui
# fix: open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
# Screen Recording MISSING needed by: screen
# fix: open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
Two grants cover the default toolsets: Accessibility for the ui_* tools and Screen Recording for screenshot, ocr and screen_find. Two things about macOS permissions that save people an hour:
- Grants attach to the app that hosts the agent — your terminal, Claude Desktop, Cursor — not to the
macos-mcpbinary. Enable that app in the panedoctorlinks to. - Restart the client after granting. Screen Recording in particular only binds to processes started after the grant.
doctor --json is the same report for tooling, and the exit code is the number of missing grants. Calendar, Reminders and Contacts (the gated apps toolset) prompt through the system dialog on first use; scripting another app with an AppleScript recipe asks for Automation the first time.
3. Register with your client
macos-mcp install --client claude-code
macos-mcp install --client claude-desktop
macos-mcp install --client cursor
macos-mcp install --client vscode
macos-mcp install --client codex
install merges a stdio server entry into the client's own config file and backs up whatever was there; --dry-run prints the target file and the resulting config without writing. For Claude Code the equivalent by hand is:
claude mcp add --transport stdio macos-mcp -- /usr/local/bin/macos-mcp serve
claude mcp list # macos-mcp: Connected
Any other MCP client gets the same stdio entry — command macos-mcp, argument serve. Restart the client and the tools appear.
4. Make the first call
Ask your agent something concrete:
- “Take a screenshot of my screen.” — proves Screen Recording, the server and the client wiring end to end.
- “Snapshot the frontmost app and click the Save button.” — the accessibility path:
ui_snapshotthenui_clickby element id, no pointer movement, no focus theft. - “Find the word ‘Cancel’ on screen and tell me where it is.” —
screen_find, OCR with fuzzy matching, for apps with no accessibility support. - “What's on my calendar this week?” — the agent calls
enable_toolsetforapps, macOS prompts once,calendar_searchanswers.
Toolsets and gating
Tools live in named toolsets. Four are on by default — ui, screen, system, files (24 of 44 tools) — which keeps the tool list small enough that models pick well. apps, intents, ios and web are one call away: the agent calls enable_toolset and the server sends list_changed, or you start with more:
macos-mcp serve --toolsets ui,screen,system,files,apps,intents,ios,web
A real tool in a disabled toolset returns a typed pointer to the toolset that has it; an unknown name stays a protocol error. The full tool reference →
Permission profiles
macos-mcp serve --profile readonly # observation only
macos-mcp serve --profile standard # default: destructive tools need a confirmation token
macos-mcp serve --profile full # trusts the caller; raw applescript_eval allowed
Under standard, a destructive tool (trash, app_quit, applescript_eval, and any mounted Apple tool that declares itself destructive) first answers confirmation_required with a one-shot token bound to that exact call; the action runs only when re-issued with the token. The safety model in full →
CLI reference
| Command | What it does |
|---|---|
macos-mcp serve | Run the MCP server over stdio. Flags: --toolsets, --profile readonly|standard|full, --gateway auto|off (mount Apple's Safari/Xcode MCP servers), --gateway-budget-seconds N (how long an upstream gets to handshake; raise it for the first Xcode mount, where a human clicks Allow inside Xcode), --config (default ~/.macos-mcp/config.json). |
macos-mcp doctor | Permission status per toolset with the System Settings deep-link for each missing grant. --json for tooling; exit code = missing grants. |
macos-mcp install --client … | Register with claude-code, claude-desktop, cursor, vscode or codex. --dry-run shows the change; the previous config is backed up. |
macos-mcp audit tail / audit summary | Read the audit log: recent calls, or totals by tool and failure code. |
macos-mcp eval run / eval list | Run the bundled agent-task suite against the real server and print the score (--driver claude has a real model solve the tasks). Skips are reported, never counted as passes. |
macos-mcp schema dump / schema check --baseline … | Snapshot the public tool surface, or diff a build against a released snapshot — the freeze that makes tool names API. |
macos-mcp vm … | Disposable macOS VMs on this host: list, clone, start, stop, destroy, status, screenshot, ip, input, grant, verify. These are CLI subcommands, not MCP tools — the tool surface is unchanged. Disposable macOS VMs → |
Troubleshooting
permission_missing— the error carries the exact deep-link infix. Grant it to the hosting app, then restart that app.- Screen Recording granted but screenshots still fail — the grant binds to new processes only. Quit and relaunch the client (or the terminal that runs it).
- An AppleScript recipe fails with an Automation hint — macOS asked “Terminal wants access to control Safari” on the Mac's own screen and is waiting for a click. There is no plus button for Automation; asking is the prompt.
action_unverified— the tool ran, the API reported success, and the UI did not change. That is macOS being honest through us; re-snapshot and try the element's own action, or the synthetic fallback, as the error suggests.- The tool list looks short — that is gating. Ask the agent to enable a toolset or pass
--toolsets.
Uninstall
rm /usr/local/bin/macos-mcp
rm -r ~/.macos-mcp # config + audit log
# then remove the macos-mcp entry from your client's MCP config
# (install left a backup next to it), and revoke the grants in
# System Settings → Privacy & Security if you no longer want them.
Next
- Tool reference — every argument, default and error code.
- Safety model — profiles, confirmation tokens, audit, hard lines.
- Fleets and VMs — disposable Macs for risky work.
- Migrating from apple-mcp — the tool map.
- macOS 27 Golden Gate — what changes for agents.