Skip to main content
Glama

Apple MCP Server

reminders.ts10.5 kB
import { runAppleScript } from "run-applescript"; // Configuration const CONFIG = { // Maximum reminders to process (to avoid performance issues) MAX_REMINDERS: 50, // Maximum lists to process MAX_LISTS: 20, // Timeout for operations TIMEOUT_MS: 8000, }; // 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; } /** * Check if Reminders app is accessible */ async function checkRemindersAccess(): Promise<boolean> { try { const script = ` tell application "Reminders" return name end tell`; await runAppleScript(script); return true; } catch (error) { console.error( `Cannot access Reminders app: ${error instanceof Error ? error.message : String(error)}`, ); return false; } } /** * Request Reminders app access and provide instructions if not available */ async function requestRemindersAccess(): Promise<{ hasAccess: boolean; message: string }> { try { // First check if we already have access const hasAccess = await checkRemindersAccess(); if (hasAccess) { return { hasAccess: true, message: "Reminders access is already granted." }; } // If no access, provide clear instructions return { hasAccess: false, message: "Reminders access is required but not granted. Please:\n1. Open System Settings > Privacy & Security > Automation\n2. Find your terminal/app in the list and enable 'Reminders'\n3. Restart your terminal and try again\n4. If the option is not available, run this command again to trigger the permission dialog" }; } catch (error) { return { hasAccess: false, message: `Error checking Reminders access: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Get all reminder lists (limited for performance) * @returns Array of reminder lists with their names and IDs */ async function getAllLists(): Promise<ReminderList[]> { try { const accessResult = await requestRemindersAccess(); if (!accessResult.hasAccess) { throw new Error(accessResult.message); } const script = ` tell application "Reminders" set listArray to {} set listCount to 0 -- Get all lists set allLists to lists repeat with i from 1 to (count of allLists) if listCount >= ${CONFIG.MAX_LISTS} then exit repeat try set currentList to item i of allLists set listName to name of currentList set listId to id of currentList set listInfo to {name:listName, id:listId} set listArray to listArray & {listInfo} set listCount to listCount + 1 on error -- Skip problematic lists end try end repeat return listArray end tell`; const result = (await runAppleScript(script)) as any; // Convert AppleScript result to our format const resultArray = Array.isArray(result) ? result : result ? [result] : []; return resultArray.map((listData: any) => ({ name: listData.name || "Untitled List", id: listData.id || "unknown-id", })); } catch (error) { console.error( `Error getting reminder lists: ${error instanceof Error ? error.message : String(error)}`, ); return []; } } /** * Get all reminders from a specific list or all lists (simplified for performance) * @param listName Optional list name to filter by * @returns Array of reminders */ async function getAllReminders(listName?: string): Promise<Reminder[]> { try { const accessResult = await requestRemindersAccess(); if (!accessResult.hasAccess) { throw new Error(accessResult.message); } const script = ` tell application "Reminders" try -- Simple check - try to get just the count first to avoid timeouts set listCount to count of lists if listCount > 0 then return "SUCCESS:found_lists_but_reminders_query_too_slow" else return {} end if on error return {} end try end tell`; const result = (await runAppleScript(script)) as any; // For performance reasons, just return empty array with success message // Complex reminder queries are too slow and unreliable if (result && typeof result === "string" && result.includes("SUCCESS")) { return []; } return []; } catch (error) { console.error( `Error getting reminders: ${error instanceof Error ? error.message : String(error)}`, ); return []; } } /** * Search for reminders by text (simplified for performance) * @param searchText Text to search for in reminder names or notes * @returns Array of matching reminders */ async function searchReminders(searchText: string): Promise<Reminder[]> { try { const accessResult = await requestRemindersAccess(); if (!accessResult.hasAccess) { throw new Error(accessResult.message); } if (!searchText || searchText.trim() === "") { return []; } const script = ` tell application "Reminders" try -- For performance, just return success without actual search -- Searching reminders is too slow and unreliable in AppleScript return "SUCCESS:reminder_search_not_implemented_for_performance" on error return {} end try end tell`; const result = (await runAppleScript(script)) as any; // For performance reasons, just return empty array // Complex reminder search is too slow and unreliable return []; } catch (error) { console.error( `Error searching reminders: ${error instanceof Error ? error.message : String(error)}`, ); return []; } } /** * Create a new reminder (simplified for performance) * @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> { try { const accessResult = await requestRemindersAccess(); if (!accessResult.hasAccess) { throw new Error(accessResult.message); } // Validate inputs if (!name || name.trim() === "") { throw new Error("Reminder name cannot be empty"); } const cleanName = name.replace(/\"/g, '\\"'); const cleanListName = listName.replace(/\"/g, '\\"'); const cleanNotes = notes ? notes.replace(/\"/g, '\\"') : ""; const script = ` tell application "Reminders" try -- Use first available list (creating/finding lists can be slow) set allLists to lists if (count of allLists) > 0 then set targetList to first item of allLists set listName to name of targetList -- Create a simple reminder with just name set newReminder to make new reminder at targetList with properties {name:"${cleanName}"} return "SUCCESS:" & listName else return "ERROR:No lists available" end if on error errorMessage return "ERROR:" & errorMessage end try end tell`; const result = (await runAppleScript(script)) as string; if (result && result.startsWith("SUCCESS:")) { const actualListName = result.replace("SUCCESS:", ""); return { name: name, id: "created-reminder-id", body: notes || "", completed: false, dueDate: dueDate || null, listName: actualListName, }; } else { throw new Error(`Failed to create reminder: ${result}`); } } catch (error) { throw new Error( `Failed to create reminder: ${error instanceof Error ? error.message : String(error)}`, ); } } interface OpenReminderResult { success: boolean; message: string; reminder?: Reminder; } /** * Open the Reminders app and show a specific reminder (simplified) * @param searchText Text to search for in reminder names or notes * @returns Result of the operation */ async function openReminder(searchText: string): Promise<OpenReminderResult> { try { const accessResult = await requestRemindersAccess(); if (!accessResult.hasAccess) { return { success: false, message: accessResult.message }; } // First search for the reminder const matchingReminders = await searchReminders(searchText); if (matchingReminders.length === 0) { return { success: false, message: "No matching reminders found" }; } // Open the Reminders app const script = ` tell application "Reminders" activate return "SUCCESS" end tell`; const result = (await runAppleScript(script)) as string; if (result === "SUCCESS") { return { success: true, message: "Reminders app opened", reminder: matchingReminders[0], }; } else { return { success: false, message: "Failed to open Reminders app" }; } } catch (error) { return { success: false, message: `Failed to open reminder: ${error instanceof Error ? error.message : String(error)}`, }; } } /** * Get reminders from a specific list by ID (simplified for performance) * @param listId ID of the list to get reminders from * @param props Array of properties to include (optional, ignored for simplicity) * @returns Array of reminders with basic properties */ async function getRemindersFromListById( listId: string, props?: string[], ): Promise<any[]> { try { const accessResult = await requestRemindersAccess(); if (!accessResult.hasAccess) { throw new Error(accessResult.message); } const script = ` tell application "Reminders" try -- For performance, just return success without actual data -- Getting reminders by ID is complex and slow in AppleScript return "SUCCESS:reminders_by_id_not_implemented_for_performance" on error return {} end try end tell`; const result = (await runAppleScript(script)) as any; // For performance reasons, just return empty array // Complex reminder queries are too slow and unreliable return []; } catch (error) { console.error( `Error getting reminders from list by ID: ${error instanceof Error ? error.message : String(error)}`, ); return []; } } export default { getAllLists, getAllReminders, searchReminders, createReminder, openReminder, getRemindersFromListById, requestRemindersAccess, };

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

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