# Claude Apple Bridges ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) ![Platform: macOS 13+](https://img.shields.io/badge/Platform-macOS%2013%2B-lightgrey.svg) ![Swift](https://img.shields.io/badge/Swift-5.9%2B-orange.svg) Swift CLI tools that give [Claude Code](https://claude.ai/claude-code) native access to Apple apps — Reminders, Calendar, Contacts, Notes, Mail, and tmux. Includes a [Claude Code skill](#skills) (`/apple-bridges`) with complete command reference for all bridges. ## Usage Examples with Claude Code Once set up, you can ask Claude naturally in any Claude Code session. Here are real-world examples: --- ### Task & Project Management > *"What's on my todo list for today?"* Claude checks your Reminders and summarizes open items with due dates and priorities. > *"I just finished the login bug fix — mark the GitHub issue and the reminder as done."* Claude closes the GitHub issue and calls `reminders-bridge complete` in one step. > *"Add a reminder to my 'Work' list to review the PR tomorrow morning."* Claude creates the reminder with a due date set to tomorrow at 9:00. --- ### Calendar-Aware Scheduling > *"I want to work on the Android release tomorrow — find a free slot and set a reminder."* Claude checks `calendar-bridge tomorrow`, finds a free window, and sets the reminder due date accordingly — no double-booking. > *"What do I have going on this week? Block some time for code review."* Claude reads your calendar for each day, spots gaps, and adds events or reminders where they fit. > *"Schedule our next planning session for next Monday at 10am in my Work calendar."* Claude calls `calendar-bridge add` directly without you having to open Calendar. --- ### Contacts Lookup > *"What's Rob's phone number?"* Claude searches your Contacts and returns the number directly — no need to open the Contacts app. > *"Add Alex to my contacts: +49 123 456789, alex@example.com"* Claude calls `contacts-bridge add` and confirms once saved. > *"Show me all details for my contact Thomas."* Claude returns name, phone, email, address and birthday in one view. --- ### Development Workflow Integration > *"Start working on issue #42 — create a reminder to track it and add the 'in progress' label."* Claude adds a GitHub label, creates a Reminders entry with the issue number in the notes, and sets a due date. > *"We finished the feature — close the issue, complete the reminder, and write the release note."* Claude handles all three in one go. > *"What are my open todos related to this project?"* Claude reads your Reminders list and cross-references with open GitHub issues for a full picture. --- ### End-of-Day / Planning > *"Summarize what we did today and create reminders for anything we didn't finish."* Claude reviews the session, identifies incomplete work, and adds follow-up reminders with appropriate due dates. > *"Set reminders for all open todos so they show up in my Calendar tomorrow after my meetings."* Claude reads tomorrow's calendar first to avoid conflicts, then sets due times in the free slots. --- ## Bridges ### reminders-bridge Access and manage Apple Reminders from Claude Code. ``` reminders-bridge lists List all reminder lists reminders-bridge create-list Create a new list reminders-bridge rename-list Rename an existing list reminders-bridge delete-list Delete a list (and all its reminders) reminders-bridge items Show all reminders in a list reminders-bridge incomplete Show only incomplete reminders reminders-bridge today Show reminders due today (all lists) reminders-bridge overdue Show all overdue reminders (all lists) reminders-bridge search Search by title/notes across all lists reminders-bridge add [notes] Add a new reminder reminders-bridge set-due <list> <title> <datetime> Set due date (YYYY-MM-DD HH:mm) reminders-bridge set-notes <list> <title> <notes> Set or update notes reminders-bridge rename <list> <oldTitle> <newTitle> Rename a reminder (title only, preserves notes/due) reminders-bridge complete <list> <title> Mark a reminder as complete reminders-bridge delete <list> <title> [--force] Delete a reminder (dry-run without --force) ``` ### calendar-bridge Read and write Apple Calendar events from Claude Code. ``` calendar-bridge calendars List all calendars calendar-bridge today Show today's events calendar-bridge tomorrow Show tomorrow's events calendar-bridge week Show this week's events calendar-bridge events <YYYY-MM-DD> Show events for a date calendar-bridge free-slots <YYYY-MM-DD> Show free time slots (08:00–20:00) calendar-bridge search <query> Search events by title (next 365 days) calendar-bridge add <cal> <title> <start> <end> Add event (YYYY-MM-DD HH:mm) calendar-bridge add-all-day <cal> <title> <YYYY-MM-DD> Add all-day event calendar-bridge delete <cal> <title> <YYYY-MM-DD> [--force] Delete event (dry-run without --force) ``` ### contacts-bridge Search and manage Apple Contacts from Claude Code. ``` contacts-bridge search <query> Search by name, email or phone contacts-bridge show <name> Show full details for a contact contacts-bridge add <firstName> <lastName> [phone] [email] Add a new contact contacts-bridge update <name> phone <value> Update phone number contacts-bridge update <name> email <value> Update email address contacts-bridge delete <name> [--force] Delete a contact (dry-run without --force) contacts-bridge birthdays-today Contacts with birthday today contacts-bridge birthdays-upcoming <days> Upcoming birthdays in next N days ``` ### notes-bridge Read and write Apple Notes from Claude Code. ``` notes-bridge accounts List all accounts notes-bridge folders [account] List folders (default: iCloud) notes-bridge list [folder] [account] List notes with modification date notes-bridge search <query> Search by title and content across all accounts notes-bridge read <title> [account] Read note content as plain text notes-bridge add <folder> <title> <body> [account] Create a new note notes-bridge append <title> <text> [account] Append text to an existing note notes-bridge delete <title> [--force] [account] Delete a note (dry-run without --force) ``` **Formatting:** The `body` and `text` parameters support HTML — Notes.app renders it natively. ```bash # Plain text note notes-bridge add "Ideas" "Shopping" "Milk, Bread, Eggs" # Rich note with HTML formatting notes-bridge add "Work" "Meeting Notes" "<b>Attendees:</b> Tobias, Heiko<br><br><ul><li>Discussed Q1 roadmap</li><li>Next steps: review PR</li></ul>" # Append a formatted section notes-bridge append "Meeting Notes" "<br><b>Follow-up:</b><br><ul><li>Send report by Friday</li></ul>" ``` Supported HTML tags: `<b>`, `<i>`, `<u>`, `<br>`, `<ul>`, `<ol>`, `<li>`, `<h1>`–`<h3>`, `<a href="...">`, `<p>` ### mail-bridge Read and send Apple Mail messages from Claude Code. ``` mail-bridge accounts List all email accounts mail-bridge mailboxes [account] List mailboxes (default: first account) mail-bridge list [mailbox|account] [account] [count] List recent messages (auto-detects account names) mail-bridge unread [mailbox|account] [account] [--all] [--max N] [--since X] List unread — fast (server-side filter); --all = every account mail-bridge search <query> [max_results] [account] [--unread] [--since X] Search subject/sender — output includes <mid:...> for each hit mail-bridge read <index> [mailbox] [account] Read message (unread status preserved) mail-bridge read <index> [mailbox] [account] --mark-read Read message and mark as read mail-bridge read <index> [mailbox] [account] --raw Read raw RFC822 source (HTML + MIME attachments) mail-bridge read --mid <message-id> [account] [--mark-read] [--raw] Read by RFC822 message-id (no listing needed) — fetches full body mail-bridge send <to> <subject> <body> [/attachment] [--from <email>] Opens compose window — user reviews and sends manually mail-bridge send <to> <subject> <body> [/attachment] [--from <email>] --force Sends directly without UI mail-bridge delete <index> [mailbox] [account] [--force] Move to Trash (dry-run without --force) ``` **`--since` accepts** `Nd` / `Nw` / `Nm` (days/weeks/months), a bare integer (days), or `YYYY-MM-DD`. **Unread / search performance:** both commands push the filter into Mail.app via a `whose` clause, so Mail scans the mailbox internally instead of the bridge fetching every message's properties over IPC. On a 50k-message INBOX the old behaviour took minutes; the filtered variant returns in ~1s per account. Use `--since 7d` to narrow further on servers that index slowly. ```bash # All unread across every account, last 7 days, cap at 20 mail-bridge unread --all --since 7d --max 20 # Only unread results when searching mail-bridge search "invoice" --unread --since 30d ``` **`--mid` example — fetch a specific message without knowing its INBOX index:** `search` now prints an `<mid:...>` token for every hit (RFC822 Message-ID). Pass it to `read --mid` to fetch the full message directly — no expensive `list` walk required, and Mail.app downloads the body if it was only partially cached. ```bash mail-bridge search "invoice" 50 work@company.com --since 90d # Found 7 message(s) (last 90d): # <mid:abc123@example.com> Invoice #4711 — billing@vendor.com (3/15) # ... mail-bridge read --mid "abc123@example.com" work@company.com --raw > /tmp/mail.eml ``` **`--raw` example — extract PDF attachments from invoice emails:** ```bash # Dump the raw message source (RFC822, includes HTML body + MIME attachments) mail-bridge read 42 INBOX work@company.com --raw > /tmp/mail.eml # Then parse with Python to save any PDF attachment python3 <<'EOF' import email raw = open('/tmp/mail.eml').read().split('\n---\n', 1)[1] msg = email.message_from_string(raw) for part in msg.walk(): fn = part.get_filename() or '' if fn.endswith('.pdf'): open(f'/tmp/{fn}', 'wb').write(part.get_payload(decode=True)) print('Saved', fn) EOF ``` Use `--raw` when the default plain-text body strips URLs, images, or attachments you need (e.g. Stripe invoice links, PDF receipts, ticket attachments). **Send examples:** ```bash # Ask Claude to draft a reply → compose window opens in Mail.app for review: mail-bridge send heiko@web.de "Betreff" "Hallo Heiko, ..." # Claude sends automatically without user interaction (explicit intent required): mail-bridge send heiko@web.de "Betreff" "Hallo Heiko, ..." --force # With attachment and explicit sender: mail-bridge send heiko@web.de "Report" "See attached." /tmp/report.pdf --from work@company.com --force ``` ### tmux-bridge Read and write tmux session contents from Claude Code — great for end-of-day summaries and interactive terminal control. ``` tmux-bridge sessions List all running sessions tmux-bridge windows [session] List windows in a session tmux-bridge panes [session] List all panes with path and command tmux-bridge read <target> [lines] Read pane content (e.g. main:1.1, default: 1000 lines) tmux-bridge write <target> <text> [--no-enter] Send keystrokes to a pane tmux-bridge snapshot [session] [lines] Capture all panes at once (default: 5000 lines) ``` **Write examples:** ```bash # Send a command (text + Enter) tmux-bridge write main:0.0 "ls -la" # Send text without pressing Enter tmux-bridge write main:0.0 "partial input" --no-enter ``` Typical workflow: run your work in named tmux sessions, then ask Claude at the end of the day: > *"Read tmux-bridge snapshot and summarize what I worked on today."* --- ## Setup ### 1. Compile ```bash # Install all bridges at once (recommended) git clone https://github.com/more-io/claude-apple-bridges.git cd claude-apple-bridges make install ``` **Optional: Persistent TCC permissions with a Developer certificate** By default, bridges are ad-hoc signed. macOS TCC may re-prompt for permissions in new terminal sessions (especially remote/SSH). If you have an Apple Developer certificate, pass `CODESIGN_IDENTITY` for persistent grants: ```bash make install CODESIGN_IDENTITY="Apple Development: Your Name (TEAMID)" ``` Or install individually: ```bash make install-reminders make install-calendar make install-contacts make install-notes make install-mail ``` <details> <summary>Manual compile (without make)</summary> ```bash # reminders-bridge cat > /tmp/reminders-info.plist << 'EOF' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSRemindersFullAccessUsageDescription</key> <string>Claude Code needs access to Reminders to manage tasks.</string> </dict> </plist> EOF swiftc reminders-bridge.swift -o ~/.claude/reminders-bridge \ -framework EventKit \ -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker /tmp/reminders-info.plist codesign --force --sign - --identifier com.claude.reminders-bridge ~/.claude/reminders-bridge # contacts-bridge cat > /tmp/contacts-info.plist << 'EOF' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSContactsUsageDescription</key> <string>Claude Code needs access to Contacts to look up and manage contacts.</string> </dict> </plist> EOF swiftc contacts-bridge.swift -o ~/.claude/contacts-bridge \ -framework Contacts \ -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker /tmp/contacts-info.plist codesign --force --sign - --identifier com.claude.contacts-bridge ~/.claude/contacts-bridge # calendar-bridge cat > /tmp/calendar-info.plist << 'EOF' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSCalendarsFullAccessUsageDescription</key> <string>Claude Code needs access to Calendar to schedule and view events.</string> </dict> </plist> EOF swiftc calendar-bridge.swift -o ~/.claude/calendar-bridge \ -framework EventKit \ -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker /tmp/calendar-info.plist codesign --force --sign - --identifier com.claude.calendar-bridge ~/.claude/calendar-bridge # notes-bridge swiftc notes-bridge.swift -o ~/.claude/notes-bridge codesign --force --sign - --identifier com.claude.notes-bridge ~/.claude/notes-bridge # mail-bridge swiftc mail-bridge.swift -o ~/.claude/mail-bridge codesign --force --sign - --identifier com.claude.mail-bridge ~/.claude/mail-bridge ``` </details> ### 2. Grant permissions Run each binary once from Terminal to trigger the macOS permission dialog: ```bash ~/.claude/reminders-bridge lists ~/.claude/calendar-bridge today ~/.claude/contacts-bridge search "test" ~/.claude/notes-bridge accounts ~/.claude/mail-bridge accounts ``` Then approve in **System Settings → Privacy & Security → Reminders / Calendars / Contacts / Automation**. Notes and Mail access is granted automatically via AppleScript on first use. ### 3. Add to Claude Code allowed tools Add the bridges to your **global** `~/.claude/settings.json` so they work across all projects without confirmation prompts: ```json { "permissions": { "allow": [ "Bash(~/.claude/reminders-bridge*)", "Bash(~/.claude/calendar-bridge*)", "Bash(~/.claude/contacts-bridge*)", "Bash(~/.claude/notes-bridge*)", "Bash(~/.claude/mail-bridge*)", "Bash(~/.claude/tmux-bridge*)" ] } } ``` --- ## Requirements - macOS 13+ - Swift (comes with Xcode or `xcode-select --install`) - Claude Code --- ## Testing After installing, run the integration test suite to verify everything works: ```bash make test ``` 35 tests cover all commands across all three bridges — exit codes, output validation, and argument handling. No data is modified during tests. --- ## Skills Skills are structured prompt templates that Claude Code can load automatically when a bridge topic comes up. Instead of relying solely on CLAUDE.md for bridge documentation, the skill provides complete command syntax, parameters, and usage examples on demand. ### What Skills Do When you type `/apple-bridges` in Claude Code (or when Claude detects you're asking about Reminders, Calendar, Contacts, Notes, Mail, or tmux), the skill loads: 1. **SKILL.md** — Overview of all bridges with a quick reference table 2. **Detail files** — Full documentation per bridge, loaded only when needed This keeps context usage minimal while giving Claude complete knowledge of every command. ### Install Skills Skills are included with `make install`: ```bash make install # Installs bridges AND skills make install-skills # Install skills only ``` This copies the skill files to `~/.claude/skills/apple-bridges/`. ### Manual Install ```bash cp -r skills/apple-bridges ~/.claude/skills/ ``` ### Verify Start a new Claude Code session and type `/apple-bridges` — it should be available as a slash command. --- ## Contributing Pull requests are welcome! When adding a new bridge: 1. Create `<name>-bridge.swift` in the repo root 2. Add compile instructions to `README.md` and `CLAUDE.md` 3. Add the permission grant step to `README.md` 4. Add usage examples to `README.md` See `CLAUDE.md` for developer notes and the branching workflow. --- ## License MIT — see [LICENSE](LICENSE) for details.