Search, read and act on Apple Calendar β ranges, free-time lookup, writes off by default
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Put your agent to work in your everyday Apple apps.
MCP servers for the Apple apps already on your Mac, and the signed app that grants them their permissions once instead of once each β for any agent that speaks MCP, not for one host.
Unofficial. Not affiliated with Apple. These drive the apps that are already on your Mac.
Look at how I write in my work inbox, then draft this reply in the same voice.
Pull together everything about the Atlas launch from my mail, my notes and my calendar. What do I still owe people?
Turn the action items from yesterday's client thread into reminders, due Friday.
Every one of those carries a constraint β an account, a date bound, a filter. That is the part the
naive osascript path answers in 74 seconds or answers wrongly, and the reason a server earns its
place: it holds what the model would otherwise re-derive every session. The measurements are in
docs/verify.md; what the alternatives cost is in
docs/alternatives.md.
The last one needs the write gate open on Reminders. Writes are off per surface until you turn them on, and the toggle decides whether the mutating tools are registered at all β an agent with writes off cannot see that they exist.
| Surface | Package | Status |
|---|---|---|
packages/mail | implemented β 21 tools, search/read/attachments + gated writes | |
| Notes | packages/notes | implemented β 13 tools, search/read/attachments + gated writes |
| Reminders | packages/reminders | implemented β 11 tools, lists/search/dates + gated writes |
| Calendar | packages/calendar | implemented β 10 tools, ranges/search/free-time + gated writes |
| Contacts | packages/contacts | implemented β 7 tools, resolves handles to names + gated writes |
| Messages | packages/messages | implemented β 9 tools, chats/search/counts/decoded text + gated send and codes |
| Safari | packages/safari | implemented β 14 tools, history/tabs/reading list/page reads + gated writes and codes |
| Maps | packages/maps | implemented β 13 tools, favourites/Guides/recents + gated writes |
| Screen | β | implemented β 3 tools, ScreenCaptureKit; served in-app, no npm package; off until switched on |
| Sound | β | implemented β 10 tools, volume/routing/speech + gated recording; in-app; off until switched on |
| Desktop | β | implemented β 16 tools, AXUIElement natively; in-app, no npm package; off until switched on |
| Simulator | β | implemented β 10 tools, an iOS Simulator's screen in iOS points; in-app; off until switched on |
| β | packages/core | shared: the osascript boundary, TCC-aware errors, ro SQLite |
Screen, Sound, Desktop and Simulator arrive switched off. Every other surface that brokers an Apple app is on when Cupertino is installed; those four are not, because Screen Recording, the microphone and Accessibility are per-process grants that reach past the surface being brokered. Desktop reaches furthest β Accessibility does not scope to a target at all, so the right to press a button in Maps is the right to press one in anything, and it additionally arrives with writes off, so it can only look until you say otherwise. Simulator holds the same grant and pins its own reach to Simulator.app, with no switch that widens it. Switch them on in the surface list if you want them.
Each surface is its own server, so a host loads only the tools it wants. Every surface that brokers
an Apple app through its store or its scripting dictionary is also its own npm package; screen,
sound, desktop and simulator are not, and could not be β the first three broker a framework
rather than an app, simulator brokers an app through Accessibility, and in every case the grant
lives in the app, so the app serves them in-process and a published package could do nothing. See
docs/screen.md, docs/desktop.md and
docs/simulator.md.
simulator is the lane for an app you are building. Simulator.app bridges the simulated device's
accessibility tree into the Mac's, so an iOS app's own controls are readable and pressable here with
no WebDriverAgent and no runner process β and the surface answers in iOS points, the space
ios_simulator_tap and ios_simulator_screenshot use, with the window scale measured on every
call rather than assumed. It reads, finds and presses; behind writes it taps, swipes, types and
presses the device's buttons. Booting, installing, launching, screenshots and push need no grant
and stay with @mgcrea/mcp-ios-simulator, which
this complements rather than copies.
desktop is the one surface that drives an interface rather than reading a store, and it does it
through AXUIElement natively rather than through osascript. That distinction is the whole
surface: every Accessibility measurement this project took before 2026-09-05 went through System
Events, one Apple Event per attribute, which is where "33.6 ms a round trip" and "~14 s for a place
card" came from. Natively the same walks cost 1.24 ms a round trip and the same place card 0.177 s.
The transport was the cost, not the API β see docs/desktop.md.
They share one bundle and one Full Disk Access grant, which is the whole reason they live together β see docs/distribution.md.
Every server that brokers an Apple app is on npm and runs straight from npx β for Claude Code, a
.mcp.json beside your project:
The packages are MIT and need no licence key. What they do need is a permission, and on npm you
grant it to whatever launches them β your editor, your terminal β which is the trade the signed
Cupertino.app exists to avoid: one Full Disk Access grant held by a
notarized binary, instead of one per host. See docs/licensing.md.
Or run them from source:
then point your host at packages/<surface>/dist/cli.js by absolute path.
Writes are off unless you ask for them β see Configuration.
Running through the menu bar app instead routes every server through the bridge, so Full Disk Access is granted to Cupertino rather than to whichever editor spawned the server:
make surfaces writes a gitignored .mcp.json at the repo root, wired for that path β it is a
developer's local working config rather than something to commit, because every entry is an
absolute path into one Mac's bundle. make on its own lists every target.
Note the different server names. Wired by hand as above, a server is apple-mail and runs under
whatever grant its host process has. Wired by Cupertino it is cupertino-mail, because that entry
points at the app's bridge and runs under the app's grant. Two names for two deployments, and you
can have both. The app only ever touches its own cupertino-* keys β an apple-mail entry
belonging to some other server is left alone.
Cupertino is machine configuration, not a project dependency, so it belongs in a per-user config:
one file each, and the equivalent of --scope user everywhere. Deliberately not --scope project, which writes an .mcp.json meant to be committed β that entry is an absolute path into a
bundle on one Mac, backed by one person's Full Disk Access grant, and it would be useless to a
teammate and unwise to offer them.
All seven are written by the app, and nothing has to be pasted into a terminal. Six keep strict JSON
and are merged into as dictionaries β five under mcpServers, Visual Studio Code under servers in
User/mcp.json, which is a different file from the JSONC settings.json it was confused with for
two releases. The seventh, ~/.codex/config.toml, is TOML full of hand-written prose and structure,
so it is never re-serialised: ClientWiringTOML replaces the lines that hold MCP servers and quotes
every other byte verbatim. ChatGPT has no row of its own because it is not a separate client β the
ChatGPT app, the Codex CLI and the Codex IDE extension all read that same file, which is the row
called "ChatGPT & Codex".
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/apple-calendar-2)<a href="https://allmcps.com/mcp/apple-calendar-2"><img src="https://allmcps.com/api/badge/apple-calendar-2?style=directory" alt="Apple Calendar on AllMCPs" /></a>