Skip to main content
Glama
neverprepared

macOS Ecosystem MCP Server

README.md
# macOS Ecosystem MCP Server

A fast, native Model Context Protocol (MCP) server for Claude that provides direct access to macOS productivity apps: **Reminders**, **Calendar**, and **Notes**.

## Why This Rewrite?

The original Node.js/TypeScript server shelled out to `osascript` for **every** tool call — adding 300–800 ms of process-startup overhead each time.

This Swift rewrite:
- Uses **EventKit** natively for Reminders and Calendar (no subprocess)
- Only shells out to `osascript` for Notes (Notes.framework is private)
- Compiles to a **single self-contained binary** — no Node.js runtime required
- Speaks the MCP stdio protocol directly via the [official Swift SDK](https://github.com/modelcontextprotocol/swift-sdk)

## Features

### šŸ—“ļø Reminders (4 tools — EventKit)
- `reminders_add` — Create reminders with title, notes, due date, priority
- `reminders_list` — List reminders, filter by list and completion status
- `reminders_complete` — Mark a reminder as completed (by ID or title)
- `reminders_search` — Search reminders by keyword

### šŸ“… Calendar (5 tools — EventKit)
- `calendar_create_event` — Create events with location, notes, alerts
- `calendar_list_events` — List events within a date range
- `calendar_find_free_time` — Find available time slots
- `calendar_update_event` — Modify existing events
- `calendar_delete_event` — Delete events

### šŸ“ Notes (3 tools — osascript)
- `notes_create` — Create notes with title and body (HTML supported)
- `notes_append` — Append content to existing notes
- `notes_search` — Search notes by keyword

### šŸ’¬ iMessage (4 tools — SQLite reads + osascript sends)
- `imessage_list_chats` — List recent conversations with participants and a last-message preview
- `imessage_read` — Read a conversation by `chat_guid` or `handle`; includes attachment paths
- `imessage_search` — Search message text across all conversations
- `imessage_send` — Send text and/or a file attachment to an **allowlisted** contact

Reads query the local `~/Library/Messages/chat.db` directly (read-only). Sends go
through AppleScript and are gated by a **fail-closed contact allowlist** — sending
is disabled until you configure one:

- Env var: `MACOS_MCP_IMESSAGE_ALLOWLIST="+15551234567,friend@example.com"`
- And/or file `~/.config/macos-mcp/imessage-allowlist.json`:
  ```json
  { "allow": ["+15551234567", "friend@example.com"] }
  ```

Phone numbers are matched loosely (last 10 digits), so formatting/country-code
differences still match. Attachments from protected directories (the config dir,
`~/Library/Messages`) are refused.

## Requirements

- macOS 13 Ventura or later
- Xcode 15+ / Swift 5.9+ (build only)
- Reminders, Calendar, Contacts, and Notes access granted in **System Settings → Privacy & Security**
- **Full Disk Access** for the app running this server (Terminal, your MCP client, etc.) —
  required to read `chat.db` and to send via Messages. Grant it under
  **System Settings → Privacy & Security → Full Disk Access**. Without it, the iMessage
  tools return an actionable error.

## Build from Source

```bash
git clone https://github.com/neverprepared/macos-ecosystem-mcp.git
cd macos-ecosystem-mcp
swift build -c release
```

The binary is placed at:
```
.build/release/macos-mcp
```

Install it system-wide:
```bash
sudo cp .build/release/macos-mcp /usr/local/bin/macos-mcp
```

## macOS Permissions

The first time the binary runs it will request access. You can also grant it in advance:

1. **System Settings → Privacy & Security → Reminders** — add `macos-mcp`
2. **System Settings → Privacy & Security → Calendars** — add `macos-mcp`
3. **System Settings → Privacy & Security → Automation → Notes** — add `macos-mcp`

## Claude Desktop Configuration

Add this to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "macos-ecosystem": {
      "command": "/usr/local/bin/macos-mcp"
    }
  }
}
```

Or using the local build path:

```json
{
  "mcpServers": {
    "macos-ecosystem": {
      "command": "/path/to/macos-ecosystem-mcp/.build/release/macos-mcp"
    }
  }
}
```

Restart Claude Desktop after editing the config.

## Tool Reference

### reminders_add
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| title | string | āœ“ | — | Reminder title |
| list | string | | "Reminders" | List name |
| notes | string | | — | Body text |
| dueDate | string | | — | ISO 8601 date-time |
| priority | none\|low\|medium\|high | | "none" | Priority |

### reminders_list
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| list | string | | — | Filter by list |
| includeCompleted | boolean | | false | Include completed |
| limit | integer | | 50 | Max results (1–100) |

### reminders_complete
| Parameter | Type | Required | Description |
|---|---|---|---|
| reminderId | string | (one of) | EventKit calendarItemIdentifier |
| title | string | (one of) | Reminder title |
| list | string | | Narrow search to list |

### reminders_search
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| query | string | āœ“ | — | Search keyword |
| list | string | | — | Restrict to list |
| includeCompleted | boolean | | false | Include completed |
| limit | integer | | 20 | Max results |

### calendar_create_event
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| title | string | āœ“ | — | Event title |
| startDate | string | āœ“ | — | ISO 8601 start |
| endDate | string | āœ“ | — | ISO 8601 end |
| calendar | string | | system default | Calendar name |
| location | string | | — | Location |
| notes | string | | — | Description |
| allDay | boolean | | false | All-day event |
| alerts | integer[] | | — | Alert offsets in minutes |

### calendar_list_events
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| startDate | string | āœ“ | — | ISO 8601 range start |
| endDate | string | āœ“ | — | ISO 8601 range end |
| calendar | string | | — | Filter by calendar |
| limit | integer | | 50 | Max results |

### calendar_find_free_time
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| date | string | āœ“ | — | ISO 8601 day to search |
| duration | integer | āœ“ | — | Required slot (15–480 min) |
| workingHoursStart | integer | | 9 | Start hour (0–23) |
| workingHoursEnd | integer | | 17 | End hour (0–23) |
| calendar | string | | — | Restrict to calendar |

### calendar_update_event
| Parameter | Type | Required | Description |
|---|---|---|---|
| eventId | string | āœ“ | EKEvent eventIdentifier |
| title | string | | New title |
| startDate | string | | New ISO 8601 start |
| endDate | string | | New ISO 8601 end |
| location | string | | New location |
| notes | string | | New description |

### calendar_delete_event
| Parameter | Type | Required | Description |
|---|---|---|---|
| eventId | string | (one of) | EKEvent eventIdentifier |
| title | string | (one of) | Event title |
| date | string | | ISO 8601 date to narrow search |

### notes_create
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| title | string | āœ“ | — | Note title |
| body | string | āœ“ | — | Note body |
| folder | string | | "Notes" | Folder name |

### notes_append
| Parameter | Type | Required | Description |
|---|---|---|---|
| noteId | string | (one of) | AppleScript note ID |
| title | string | (one of) | Note title |
| folder | string | | Narrow search |
| content | string | āœ“ | Content to append |

### notes_search
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| query | string | āœ“ | — | Search keyword |
| folder | string | | — | Restrict to folder |
| limit | integer | | 20 | Max results |

## Architecture

```
Sources/macos-mcp/
ā”œā”€ā”€ App.swift              # @main entry — MCP server setup and tool dispatcher
ā”œā”€ā”€ ToolDefinitions.swift  # JSON Schema definitions for all 12 tools
ā”œā”€ā”€ EventKitManager.swift  # Swift actor wrapping EKEventStore (reminders + calendar)
└── NotesHandler.swift     # osascript runner for Notes operations
```

## Notes on Flagged Reminders

EventKit does not expose the "flagged" status of reminders (it is a Reminders-app-specific attribute not in the EventKit API). The `flagged` parameter is accepted but silently ignored when creating reminders.

## License

MIT

TDQS

A3.8/5.0

Scored across 12 tools

Disambiguation5/5

Every tool has a clearly distinct purpose with no ambiguity, as each is scoped to a specific app (calendar, notes, reminders) and action (create, delete, update, list/search, etc.). For example, calendar_find_free_time is unique for scheduling, while notes_append is distinct from notes_create. The descriptions reinforce this by detailing specific functionalities, ensuring agents can easily differentiate between tools.

Naming Consistency5/5

Tool names follow a highly consistent verb_noun pattern throughout, with a clear structure of app_action (e.g., calendar_create_event, notes_search). All tools use snake_case uniformly, and the naming convention is predictable, making it easy for agents to infer functionality. There are no deviations in style or verb usage across the set.

Tool Count5/5

The 12 tools are well-scoped for the macOS ecosystem server, covering three key apps (calendar, notes, reminders) with a balanced set of operations. Each tool earns its place by providing essential CRUD and utility functions, such as calendar_find_free_time for scheduling. The count is neither too sparse nor bloated, fitting the domain appropriately.

Completeness5/5

The tool set offers complete CRUD/lifecycle coverage for each app domain, with no obvious gaps. For calendar, it includes create, delete, update, list, and find_free_time. For notes, it covers create, append, and search. For reminders, it has add, complete, list, and search. This comprehensive surface allows agents to handle all core workflows without dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues