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;
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the operations (search, create, open) but fails to disclose critical traits like required permissions (e.g., app access), side effects (e.g., data persistence), rate limits, or error handling. For a multi-operation tool with potential mutations, this is a significant gap.

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

Conciseness5/5

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

The description is a single, efficient sentence that front-loads the core functionality ('search, create, and open reminders') without any wasted words. It's appropriately sized for the tool's scope, making it easy to parse quickly.

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?

Given the tool's complexity (8 parameters, multiple operations including mutations) and lack of annotations and output schema, the description is incomplete. It doesn't cover behavioral aspects, usage context, or return values, leaving significant gaps for an agent to operate effectively.

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?

The schema description coverage is 100%, with detailed parameter descriptions in the input schema. The description adds no additional meaning beyond the schema, such as explaining inter-parameter dependencies or usage examples. Given the high schema coverage, the baseline score of 3 is appropriate, as the schema does the heavy lifting.

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

Purpose4/5

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

The description clearly states the tool's purpose with specific verbs ('search, create, and open') and resource ('reminders in Apple Reminders app'), making it immediately understandable. However, it doesn't explicitly distinguish this tool from its siblings like 'calendar' or 'notes', which might also manage time-based or task-related data, missing full differentiation.

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?

The description provides no guidance on when to use this tool versus alternatives, such as its sibling tools (e.g., 'calendar' for events, 'notes' for general notes). It lacks explicit context, prerequisites, or exclusions, leaving the agent to infer usage based on the tool name alone.

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

Install Server

Other Tools

Latest Blog Posts

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

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