Files
Eric Jungbauer 7e619d0454 Initial commit — Apple Apps MCP server with native macOS config app
Python MCP server (FastMCP) providing Claude Code/CoWork access to Apple
Reminders, Calendar, Mail, Contacts, Find My, and Maps via AppleScript.
Includes a native SwiftUI config/installer app and a compiled EventKit
helper for fast reminder queries on large databases (8,000+ items).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:32:24 -06:00

164 lines
5.2 KiB
Python

"""Apple Reminders tools — uses compiled EventKit helper for speed."""
import json
import subprocess
from pathlib import Path
from helpers import run_applescript, safe_applescript_string
HELPER_BIN = Path(__file__).parent.parent / "helpers" / "reminders_helper"
def _run_helper(*args: str, timeout: int = 15) -> dict | list:
"""Run the compiled EventKit reminders helper and return parsed JSON."""
result = subprocess.run(
[str(HELPER_BIN)] + list(args),
capture_output=True, text=True, timeout=timeout,
)
if result.returncode != 0:
stderr = result.stderr.strip()
try:
err = json.loads(result.stdout)
raise RuntimeError(err.get("error", stderr))
except (json.JSONDecodeError, TypeError):
raise RuntimeError(f"Reminders helper error: {stderr or result.stdout}")
return json.loads(result.stdout)
def list_lists() -> list[dict]:
"""List all reminder lists (e.g., Reminders, Shopping, Work)."""
return _run_helper("lists")
def get_reminders(list_name: str | None = None, include_completed: bool = False) -> dict:
"""Get reminders, optionally filtered by list name.
Args:
list_name: Filter to a specific list. None = all lists.
include_completed: Include completed reminders.
"""
args = ["get"]
if list_name:
args += ["--list", list_name]
if not include_completed:
pass # Default is incomplete only
return _run_helper(*args, timeout=30)
def search_reminders(query: str) -> dict:
"""Search reminders by name across all lists."""
return _run_helper("search", query, timeout=30)
def get_overdue_reminders() -> dict:
"""Get all overdue (past due) incomplete reminders."""
return _run_helper("get", "--overdue")
def get_due_today() -> dict:
"""Get reminders due today."""
return _run_helper("get", "--due-today")
def get_due_this_week() -> dict:
"""Get reminders due in the next 7 days."""
return _run_helper("get", "--due-this-week")
def create_reminder(name: str, list_name: str = "Reminders", due_date: str | None = None,
body: str | None = None, priority: int = 0) -> dict:
"""Create a new reminder."""
safe_name = safe_applescript_string(name)
safe_list = safe_applescript_string(list_name)
props = [f'name:"{safe_name}"']
if body:
props.append(f'body:"{safe_applescript_string(body)}"')
if priority > 0:
props.append(f'priority:{priority}')
props_str = ", ".join(props)
due_line = ""
if due_date:
due_line = f'''
set due date of newReminder to date "{due_date}"'''
script = f'''
tell application "Reminders"
set theList to list "{safe_list}"
set newReminder to make new reminder at end of theList with properties {{{props_str}}}
{due_line}
return id of newReminder
end tell'''
reminder_id = run_applescript(script)
return {"id": reminder_id.strip(), "name": name, "list": list_name, "created": True}
def complete_reminder(name: str, list_name: str | None = None) -> dict:
"""Mark a reminder as completed by name."""
safe_name = safe_applescript_string(name)
if list_name:
safe_list = safe_applescript_string(list_name)
script = f'''
tell application "Reminders"
set theList to list "{safe_list}"
set theReminders to (every reminder of theList whose name is "{safe_name}" and completed is false)
if (count of theReminders) > 0 then
set completed of item 1 of theReminders to true
return "completed"
else
return "not_found"
end if
end tell'''
else:
script = f'''
tell application "Reminders"
repeat with L in lists
set theReminders to (every reminder of L whose name is "{safe_name}" and completed is false)
if (count of theReminders) > 0 then
set completed of item 1 of theReminders to true
return "completed"
end if
end repeat
return "not_found"
end tell'''
result = run_applescript(script)
if result == "completed":
return {"name": name, "completed": True}
raise RuntimeError(f"Reminder '{name}' not found or already completed")
def delete_reminder(name: str, list_name: str | None = None) -> dict:
"""Delete a reminder by name."""
safe_name = safe_applescript_string(name)
if list_name:
safe_list = safe_applescript_string(list_name)
script = f'''
tell application "Reminders"
set theList to list "{safe_list}"
set theReminders to (every reminder of theList whose name is "{safe_name}")
if (count of theReminders) > 0 then
delete item 1 of theReminders
return "deleted"
else
return "not_found"
end if
end tell'''
else:
script = f'''
tell application "Reminders"
repeat with L in lists
set theReminders to (every reminder of L whose name is "{safe_name}")
if (count of theReminders) > 0 then
delete item 1 of theReminders
return "deleted"
end if
end repeat
return "not_found"
end tell'''
result = run_applescript(script)
if result == "deleted":
return {"name": name, "deleted": True}
raise RuntimeError(f"Reminder '{name}' not found")