Skip to main content
Glama
jxnl
by jxnl

reminders

Search, create, and open reminders in Apple Reminders app to manage tasks and to-do lists directly through the MCP server interface.

Instructions

Search, create, and open reminders in Apple Reminders app

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
operationYesOperation to perform: 'list', 'search', 'open', 'create', or 'listById'
searchTextNoText to search for in reminders (required for search and open operations)
nameNoName of the reminder to create (required for create operation)
listNameNoName of the list to create the reminder in (optional for create operation)
listIdNoID of the list to get reminders from (required for listById operation)
propsNoProperties to include in the reminders (optional for listById operation)
notesNoAdditional notes for the reminder (optional for create operation)
dueDateNoDue date for the reminder in ISO format (optional for create operation)

Implementation Reference

  • Defines the input schema and metadata for the 'reminders' tool, specifying supported operations: list, search, open, create, listById.
    const REMINDERS_TOOL: Tool = {
      name: "reminders",
      description: "Search, create, and open reminders in Apple Reminders app",
      inputSchema: {
        type: "object",
        properties: {
          operation: {
            type: "string",
            description: "Operation to perform: 'list', 'search', 'open', 'create', or 'listById'",
            enum: ["list", "search", "open", "create", "listById"]
          },
          searchText: {
            type: "string",
            description: "Text to search for in reminders (required for search and open operations)"
          },
          name: {
            type: "string",
            description: "Name of the reminder to create (required for create operation)"
          },
          listName: {
            type: "string",
            description: "Name of the list to create the reminder in (optional for create operation)"
          },
          listId: {
            type: "string",
            description: "ID of the list to get reminders from (required for listById operation)"
          },
          props: {
            type: "array",
            items: {
              type: "string"
            },
            description: "Properties to include in the reminders (optional for listById operation)"
          },
          notes: {
            type: "string",
            description: "Additional notes for the reminder (optional for create operation)"
          },
          dueDate: {
            type: "string",
            description: "Due date for the reminder in ISO format (optional for create operation)"
          }
        },
        required: ["operation"]
      }
    };
  • tools.ts:308-308 (registration)
    Registers the REMINDERS_TOOL as part of the exported tools array.
    const tools = [CONTACTS_TOOL, NOTES_TOOL, MESSAGES_TOOL, MAIL_TOOL, REMINDERS_TOOL, WEB_SEARCH_TOOL, CALENDAR_TOOL, MAPS_TOOL];
  • Helper function to retrieve reminders from a specific list by ID, corresponding to 'listById' operation.
    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 });
    }
  • Helper function to search reminders by text in name or body, for 'search' operation.
    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[];
    }
  • Helper function to create a new reminder in a specified list, for 'create' operation.
    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;
    }

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

C2.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden but only mentions operations without behavioral details. It doesn't disclose permissions needed, rate limits, whether operations are destructive (e.g., 'create' modifies data), or how 'open' behaves. This leaves critical behavioral traits unspecified for a multi-operation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads key operations. It avoids redundancy but could be more structured by clarifying operation distinctions. Every word earns its place, though it's slightly under-specified for a multi-operation tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 8 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain return values, error conditions, or how operations interact with parameters (e.g., which params are needed for each operation). Given the complexity, more context is needed for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so parameters are well-documented in the schema. The description adds no additional meaning beyond listing operations, which the schema's 'operation' enum already covers. Baseline 3 is appropriate as the schema handles parameter semantics effectively.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the tool can 'Search, create, and open reminders in Apple Reminders app', which provides a general purpose but lacks specificity about what 'reminders' are or how operations differ. It distinguishes from siblings like 'calendar' or 'notes' by mentioning 'Apple Reminders app', but doesn't clarify functional boundaries between operations (e.g., 'list' vs 'listById').

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like 'notes' or 'calendar', or when to choose between operations (e.g., 'list' vs 'search'). The description lists operations but doesn't provide context for selection, leaving usage decisions to the agent without support.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools