UNPKG

@chrimerss/apple-mcp-enhanced

Version:

Enhanced Apple MCP tools with email folder management, contacts, notes, messages, and mail integration

310 lines (275 loc) 8.52 kB
import { run } from "@jxa/run"; // Define types for our reminders interface ReminderList { name: string; id: string; } interface Reminder { name: string; id: string; body: string; completed: boolean; dueDate: string | null; listName: string; completionDate?: string | null; creationDate?: string | null; modificationDate?: string | null; remindMeDate?: string | null; priority?: number; } /** * Get all reminder lists * @returns Array of reminder lists with their names and IDs */ async function getAllLists(): Promise<ReminderList[]> { const lists = await run(() => { const Reminders = Application("Reminders"); const lists = Reminders.lists(); return lists.map((list: any) => ({ name: list.name(), id: list.id(), })); }); return lists as ReminderList[]; } /** * Get reminders from a specific list by ID with customizable properties * @param listId ID of the list to get reminders from * @param props Array of properties to include (optional) * @returns Array of reminders with specified properties */ async function getRemindersFromListById( listId: string, props?: string[] ): Promise<any[]> { return await run( (args: { id: string; props?: string[] }) => { function main() { const reminders = Application("Reminders"); const list = reminders.lists.byId(args.id).reminders; const props = args.props || [ "name", "body", "id", "completed", "completionDate", "creationDate", "dueDate", "modificationDate", "remindMeDate", "priority", ]; // We could traverse all reminders and for each one get the all the props. // This is more inefficient than calling '.name()' on the very reminder list. It requires // less function calls. const propFns: Record<string, any[]> = props.reduce( (obj: Record<string, any[]>, prop: string) => { obj[prop] = list[prop](); return obj; }, {} ); const finalList = []; // Flatten the object {name: string[], id: string[]} to an array of form // [{name: string, id: string}, ..., {name: string, id: string}] which represents the list // of reminders for (let i = 0; i < (propFns.name?.length || 0); i++) { const reminder = props.reduce( (obj: Record<string, any>, prop: string) => { obj[prop] = propFns[prop][i]; return obj; }, {} ); finalList.push(reminder); } return finalList; } return main(); }, { id: listId, props } ); } /** * Get all reminders from a specific list or all lists * @param listName Optional list name to filter by * @returns Array of reminders */ async function getAllReminders(listName?: string): Promise<Reminder[]> { const reminders = await run((listName: string | undefined) => { const Reminders = Application("Reminders"); let allReminders: Reminder[] = []; if (listName) { // Get reminders from a specific list const lists = Reminders.lists.whose({ name: listName })(); if (lists.length > 0) { const list = lists[0]; allReminders = list.reminders().map((reminder: any) => ({ name: reminder.name(), id: reminder.id(), body: reminder.body() || "", completed: reminder.completed(), dueDate: reminder.dueDate() ? reminder.dueDate().toISOString() : null, listName: list.name(), })); } } else { // Get reminders from all lists const lists = Reminders.lists(); for (const list of lists) { const remindersInList = list.reminders().map((reminder: any) => ({ name: reminder.name(), id: reminder.id(), body: reminder.body() || "", completed: reminder.completed(), dueDate: reminder.dueDate() ? reminder.dueDate().toISOString() : null, listName: list.name(), })); allReminders = allReminders.concat(remindersInList); } } return allReminders; }, listName); return reminders as Reminder[]; } /** * Search for reminders by text * @param searchText Text to search for in reminder names or notes * @returns Array of matching reminders */ async function searchReminders(searchText: string): Promise<Reminder[]> { const reminders = await run((searchText: string) => { const Reminders = Application("Reminders"); const lists = Reminders.lists(); let matchingReminders: Reminder[] = []; for (const list of lists) { // Search in reminder names and bodies const remindersInList = list.reminders.whose({ _or: [ { name: { _contains: searchText } }, { body: { _contains: searchText } }, ], })(); if (remindersInList.length > 0) { const mappedReminders = remindersInList.map((reminder: any) => ({ name: reminder.name(), id: reminder.id(), body: reminder.body() || "", completed: reminder.completed(), dueDate: reminder.dueDate() ? reminder.dueDate().toISOString() : null, listName: list.name(), })); matchingReminders = matchingReminders.concat(mappedReminders); } } return matchingReminders; }, searchText); return reminders as Reminder[]; } /** * Create a new reminder * @param name Name of the reminder * @param listName Name of the list to add the reminder to (creates if doesn't exist) * @param notes Optional notes for the reminder * @param dueDate Optional due date for the reminder (ISO string) * @returns The created reminder */ async function createReminder( name: string, listName: string = "Reminders", notes?: string, dueDate?: string ): Promise<Reminder> { const result = await run( ( name: string, listName: string, notes: string | undefined, dueDate: string | undefined ) => { const Reminders = Application("Reminders"); // Find or create the list let list; const existingLists = Reminders.lists.whose({ name: listName })(); if (existingLists.length > 0) { list = existingLists[0]; } else { // Create a new list if it doesn't exist list = Reminders.make({ new: "list", withProperties: { name: listName }, }); } // Create the reminder properties const reminderProps: any = { name: name, }; if (notes) { reminderProps.body = notes; } if (dueDate) { reminderProps.dueDate = new Date(dueDate); } // Create the reminder const newReminder = list.make({ new: "reminder", withProperties: reminderProps, }); return { name: newReminder.name(), id: newReminder.id(), body: newReminder.body() || "", completed: newReminder.completed(), dueDate: newReminder.dueDate() ? newReminder.dueDate().toISOString() : null, listName: list.name(), }; }, name, listName, notes, dueDate ); return result as Reminder; } interface OpenReminderResult { success: boolean; message: string; reminder?: Reminder; } /** * Open the Reminders app and show a specific reminder * @param searchText Text to search for in reminder names or notes * @returns Result of the operation */ async function openReminder(searchText: string): Promise<OpenReminderResult> { // First search for the reminder const matchingReminders = await searchReminders(searchText); if (matchingReminders.length === 0) { return { success: false, message: "No matching reminders found" }; } // Open the first matching reminder const reminder = matchingReminders[0]; await run((reminderId: string) => { const Reminders = Application("Reminders"); Reminders.activate(); // Try to show the reminder // Note: This is a best effort as there's no direct way to show a specific reminder // We'll just open the app and return the reminder details return true; }, reminder.id); return { success: true, message: "Reminders app opened", reminder, }; } export default { getAllLists, getAllReminders, searchReminders, createReminder, openReminder, getRemindersFromListById, };