Apple MCP Server
by Dhravya
Verified
- apple-mcp
- utils
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 };