mail-calendar-mcp
This server provides full read/write control over Apple Mail.app and Calendar.app on macOS, exposing all configured accounts (iCloud, Google, Exchange, IMAP, etc.) via unified MCP tools.
š¬ Mail
Reading: List accounts, mailboxes (full folder trees), messages, attachments, signatures, and rules. Search/filter by read/flagged status, sender, subject. Retrieve full message content, raw RFC822 source, and headers.
Writing: Create, rename, delete mailboxes (rename/delete unreliable on cloud-synced accounts). Create, update, delete drafts. Mark read/unread, flag/unflag, set flag color. Move or copy messages. Delete to Trash or permanently (
confirm:truerequired). Send emails (confirm:truerequired; specifyfromAddressto avoid wrong account). Create, update, delete signatures.Limitations: Mail rules are read-only (platform limitation). Adding attachments to drafts is unverified.
š Calendar
Reading: List calendars (with index for disambiguation), events (bounded date range, paginated), search events by text. Get individual events by UID with full detail (attendees, alarms, recurrence). Compute free/busy availability across calendars.
Writing: Create, rename, delete calendars (
confirm:truerequired for delete). Create/update events with recurrence, location, all-day flag, alarms; per-occurrence vs. whole-series edits supported. Delete events including recurring (confirm:truerequired). Add/remove alarms. Add/remove attendees (removal is experimental).
š§ Cross-Cutting
Health-check tools (
mail_app_status,calendar_app_status) for diagnosing app state.All destructive/irreversible operations require explicit
confirm:true.Dual backend for Calendar: native Swift/EventKit by default, JXA fallback if permissions are missing.
Timeouts with SIGKILL prevent hangs from offline accounts or unbounded queries.
Structured error codes (
APP_NOT_RUNNING,PLATFORM_LIMITATION,TIMEOUT, etc.) for robustness.Injection-safe: JXA calls use fixed script files with JSON arguments.
Provides full read/write control over Apple Mail.app and Calendar.app on macOS, enabling management of emails, mailboxes, calendars, events, and related operations.
Integrates with Google accounts configured in Mail.app and Calendar.app, enabling email and calendar operations through Google.
Integrates with iCloud accounts configured in Mail.app and Calendar.app, allowing email and calendar operations through iCloud.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mail-calendar-mcpshow me my unread emails"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mail-calendar-mcp
A local MCP server giving full read/write control over Apple Mail.app and Calendar.app on macOS ā every account configured in either app (iCloud, Google, Exchange, IMAP, etc.) is exposed uniformly, with no per-provider setup and no GUI/computer-use automation. All automation is done via JXA (osascript -l JavaScript) talking to the same AppleScript dictionaries Mail.app/Calendar.app themselves expose.
Status
Feature-complete: all of Mail and Calendar's core scriptable surface is implemented, plus computed availability. Every tool below was exercised through the real MCP protocol against a live Mail.app/Calendar.app during development, not just type-checked ā several real bugs were found and fixed or explicitly documented as platform limitations in the process (see below).
Calendar runs on a native EventKit backend by default (helper/main.swift, compiled to bin/calendar-helper by npm run build). This exists because Calendar.app's AppleScript bridge silently no-ops on several operations. The native backend fixes the confirmed recurring-event-delete and calendar-delete bugs, and adds capabilities AppleScript cannot express at all: per-occurrence vs whole-series edits/deletes (occurrenceDate + span), stable calendarIds, calendar source selection, richer event detail (attendees/alarms/detached-occurrence status), and cross-calendar queries in one call. If the helper binary is missing or lacks Calendar permission, calendar tools fall back to the JXA path automatically (safe even for writes ā the permission check runs before any operation). MCM_CALENDAR_BACKEND=jxa forces the old path. Mail remains JXA-only: there is no public native API for Mail.app automation.
Related MCP server: apple-mail-mcp
Tool inventory
Tool | Notes |
| Read-only |
| Works reliably |
| Confirmed broken ā Mail rejects the property assignment. Rename manually in Mail.app. |
| Confirmed broken on cloud-synced accounts (iCloud tested) ā untested on a local "On My Mac" mailbox. |
| Read-only, paginated (default 50/max 500) |
| Works reliably |
| Moves to Trash, recoverable |
|
|
| Requires |
| Unverified ā the JXA attachment-insertion syntax could not be live-tested; treat with suspicion |
|
|
| Works reliably |
| Read-only by design ā see permanent limitations below |
Calendar
Tool | Notes |
| Read-only. Returns an |
| Works reliably. Create accepts |
| Calendar accounts/sources, for |
| Reliable via the EventKit backend. (JXA fallback confirmed broken for this ā expect |
| Read-only. |
| By |
| Reliable. EventKit backend adds |
| Reliable via the EventKit backend, including recurring events and per-occurrence deletes ( |
| Works reliably (display/sound/mail kinds) |
| Worked reliably in testing (better than pessimistic community reports this was designed against). Adding an attendee surfaces an extra |
| Unverified ā implemented but not live-tested |
| Computed locally (not a native Calendar.app feature) from busy intervals across the calendars you specify |
Cross-cutting
Tool | Notes |
| Health-check ā distinguishes "app not running" from a genuine data error |
Setup
npm install
npm run build # or `npm run dev` for a live tsx run during development
npm test # runs the vitest suite (Node-side logic only, see Testing below)Add to your MCP client config (Claude Desktop / Claude Code) pointing at dist/index.js (after npm run build) or src/index.ts via npx tsx for development.
Building requires the Xcode Command Line Tools (swiftc) for the native calendar helper.
One-time macOS permission grants
Two separate macOS permissions are involved (both one-time, both per host app ā the app that spawns the server, e.g. Claude Desktop or your terminal):
Automation (Mail + Calendar via JXA): the first JXA call triggers "
<host>wants to controlMail/Calendar" ā click OK. Re-enable under System Settings ā Privacy & Security ā Automation if ever denied.Calendars ā Full Access (native EventKit backend): the host app needs Full Access under System Settings ā Privacy & Security ā Calendars. Note macOS may silently grant only "Add Events Only" without ever prompting ā if
calendar_app_statusreports the helper unauthorized, flip that entry to Full Access manually. Until then, calendar tools transparently fall back to JXA.
If a tool call fails with error code AUTOMATION_NOT_AUTHORIZED, these are the settings to check. The ad-hoc code signature on bin/calendar-helper changes on every rebuild, which can re-trigger the Calendars grant.
Important gotcha: fromAddress
Creating a draft or sending without specifying fromAddress silently uses Mail.app's global default "send new messages from" account ā confirmed during development that this is not necessarily the account you intended (on the development machine it defaulted to an unrelated work account rather than the personal account being targeted). fromAddress is a required field on mail_create_draft and on mail_send_message when composing fresh (not required when sending an existing composeSessionId, which already has an account fixed).
Design notes
Injection-safe: every JXA invocation uses
execFile('osascript', ['-l','JavaScript', <fixed script path>, <JSON string>])ā never a shell string, never AppleScript source built from caller input. Seesrc/jxa/runner.ts.Timeouts: every call has a per-operation timeout with
SIGKILLon expiry, since Mail/Calendar can hang against an offline/asleep account or an unbounded query. Verified against both a deliberately-hung script and a real unbounded Calendar query during development.Error taxonomy: see
src/errors.tsā tool failures return one of a fixed set of codes (APP_NOT_RUNNING,AUTOMATION_NOT_AUTHORIZED,ACCOUNT_OFFLINE_OR_ASLEEP,NOT_FOUND,TIMEOUT,PLATFORM_LIMITATION,INVALID_INPUT,UNEXPECTED_OSA_ERROR) rather than opaque strings.PLATFORM_LIMITATIONspecifically covers writes that report success but don't actually take effect ā caught by re-reading state after every mutating call.Destructive-tool gating: the highest-risk tools (permanent delete, send, delete calendar/mailbox) require an explicit
confirm: trueargument in addition to whatever gating your MCP client applies, as defense-in-depth against an accidental or hallucinated call. SeerequireConfirm()insrc/tools/register.ts.Object identity: Mail messages are addressed by
{accountName, mailboxPath, messageId}(Mail's internal numeric id) or by RFC822 Message-ID; mailboxes by hierarchical name path; Calendar events by their iCalendaruid(stable via scripting, unlike calendars themselves, which have no queryable uid ā hence thecalendarIndexdisambiguator).Native Swift/EventKit helper: originally deferred (most JXA calendar operations worked in initial testing), later built once the JXA bridge's failures proved painful in real use ā see the Status section.
helper/main.swiftis a single-file CLI compiled bynpm run build:helperwith an embedded Info.plist (linker-sectcreate) and an ad-hoc code signature; it speaks the same JSON-envelope contract as the JXA scripts, sosrc/helper/backend.tscan route per-call with JXA fallback. EventKit's public API cannot write attendees, so attendee add/remove stays on JXA regardless of backend.
Known permanent platform limitations
(Not bugs ā confirmed not achievable via AppleScript/JXA, and in most cases no alternative API exists either.)
Mail: creating/editing Mail rules (rules can only invoke a script as an action, not be created externally ā
mail_list_rulesis read-only by design); mail account creation/credential management.Calendar: sending or RSVP-replying to meeting invitations; to-dos (moved entirely to Reminders.app, a separate app/dictionary, out of scope here).
Known confirmed-broken/flaky operations
(Found via live testing, not guessed ā reproduced deterministically before being documented.)
mail_rename_mailboxā the property assignment is rejected outright.mail_delete_mailboxā fails on cloud-synced accounts (confirmed on iCloud); untested on local mailboxes.mail_add_attachment_to_draftā syntax unverified, no live test possible; treat with suspicion.calendar_delete_calendarā fixed by the EventKit backend; the JXA fallback remains broken.calendar_delete_eventon a recurring event ā fixed by the EventKit backend; the JXA fallback reports success without actually deleting (silently wrong unless caught, which is why the JXA path verifies its result before reporting success).calendar_remove_attendeeā implemented but not live-tested; treat with suspicion until verified. (EventKit's public API cannot write attendees at all, so this stays on JXA regardless of backend.)
Testing
There's no way to unit-test "did this actually change Mail.app/Calendar.app state" outside a live session, so verification is two-tiered:
Manual checklists (
test/manual-checklists/) ā the primary verification method, run against dedicated scratch fixtures so destructive-op testing never touches real mail/events:Mail: a
Testing/MCP-Scratchmailbox on a non-work account, all send-tests target only your own address.Calendar: a local
MCP-Scratchcalendar ("On My Mac", to avoid sync churn).One leftover artifact from development still needs manual cleanup: an empty
MCP-Delete-Testmailbox in iCloud Mail (mailbox deletion via scripting is broken, see above). Two recurring test events stuck in theMCP-Scratchcalendar ā the JXA delete bug's own evidence ā are removable via the EventKit backend.
npm test(vitest) ā covers only the Node-side logic that doesn't need live app state: error-code mapping (test/errors.test.ts), destructive-tool confirm-gating (test/register.test.ts), JSON envelope parsing robustness (test/jxa-runner.test.ts), and free/busy gap computation (test/availability.test.ts).
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mathewcsims/mail-calendar-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server