Skip to main content
Glama

Apple MCP Tools

by wearesage
reminders.ts10.1 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, dueDateString?: string // Rename parameter to avoid confusion ): Promise<Reminder> { // Convert ISO string to a simple date string that JXA can handle let dueDateForJXA: string | undefined; if (dueDateString) { try { // Parse the date string const jsDate = new Date(dueDateString); // Check if the date is valid if (isNaN(jsDate.getTime())) { throw new Error(`Invalid date string provided: ${dueDateString}`); } // Format the date in a way that JXA can understand // Using a simple format: YYYY-MM-DD HH:MM:SS const year = jsDate.getFullYear(); const month = String(jsDate.getMonth() + 1).padStart(2, '0'); const day = String(jsDate.getDate()).padStart(2, '0'); const hours = String(jsDate.getHours()).padStart(2, '0'); const minutes = String(jsDate.getMinutes()).padStart(2, '0'); const seconds = String(jsDate.getSeconds()).padStart(2, '0'); dueDateForJXA = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } catch (e) { throw new Error( `Invalid due date format: ${dueDateString}. ` + `Please use ISO 8601 format (e.g., YYYY-MM-DDTHH:MM:SS.sssZ or YYYY-MM-DD).` ); } } const result = await run( ( name: string, listName: string, notes: string | undefined, dueDateString: string | undefined // Pass date as a string instead of Date object ) => { 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; } // Use the passed date string with additional error handling if (dueDateString) { try { // Convert the string to a Date object in JXA context reminderProps.dueDate = new Date(dueDateString); } catch (e) { // Continue without the due date rather than failing completely } } // 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, dueDateForJXA // Pass the formatted date string ); 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, };

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/wearesage/mcp-apple'

If you have feedback or need assistance with the MCP directory API, please join our Discord server