add_todo
Create a new to-do item in Things.app with title, notes, tags, checklist items, and assign to projects or areas for task management.
Instructions
Create a new to-do item in Things.app. Add notes, tags, checklist items, and assign to projects or areas.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | To-do title (required). Clear, actionable description of the task | |
| notes | No | Additional notes or details for the to-do (max 10,000 characters). Supports markdown formatting for rich text | |
| when | No | Schedule the to-do for a specific time. Use "today" for immediate action, "tomorrow" for next day, "evening" for later today, "anytime" for no specific time, "someday" for future consideration, or ISO date format (YYYY-MM-DD) for specific date | |
| deadline | No | Set a deadline for the to-do in ISO date format (YYYY-MM-DD). Creates a deadline reminder in Things.app | |
| tags | No | Array of tag names for organizing and categorizing the to-do (max 20 tags). Tags help with filtering and organization | |
| checklistItems | No | Array of checklist item descriptions to add as sub-tasks (max 100 items). Each item becomes a checkable sub-task within the to-do | |
| projectId | No | ID of the project to add this to-do to. Use this when you know the specific project ID | |
| projectName | No | Name of the project to add this to-do to. Things.app will find the project by name and add the to-do there | |
| areaId | No | ID of the area of responsibility to assign this to-do to. Use this when you know the specific area ID | |
| areaName | No | Name of the area of responsibility to assign this to-do to (e.g., "Work", "Personal", "Health") | |
| headingId | No | ID of a specific heading within the target project to organize the to-do under | |
| headingName | No | Name of a heading within the target project to organize the to-do under (e.g., "Phase 1", "Research") | |
| completed | No | Mark the to-do as completed immediately upon creation (default: false). Useful for logging already completed tasks | |
| canceled | No | Mark the to-do as canceled immediately upon creation (default: false). Useful for recording tasks that are no longer needed | |
| creationDate | No | Override the creation date with a specific ISO8601 datetime (YYYY-MM-DDTHH:MM:SS). Useful for importing historical data | |
| completionDate | No | Set a specific completion date using ISO8601 datetime (YYYY-MM-DDTHH:MM:SS). Only used when completed is true |
Implementation Reference
- src/tools/add-todo.ts:66-106 (handler)The asynchronous handler function that maps input parameters to Things.app URL scheme parameters, constructs the URL, opens it to create the todo, handles errors, and returns a success response.async (params) => { try { logger.info('Adding new to-do', { title: params.title }); const urlParams: Record<string, any> = { title: params.title }; // Map schema parameters to Things URL scheme parameters if (params.notes) urlParams.notes = params.notes; if (params.when) urlParams.when = params.when; if (params.deadline) urlParams.deadline = params.deadline; if (params.tags) urlParams.tags = params.tags.join(','); if (params.checklistItems) urlParams['checklist-items'] = params.checklistItems.join(','); if (params.projectId) urlParams['list-id'] = params.projectId; if (params.projectName) urlParams.list = params.projectName; if (params.areaId) urlParams['area-id'] = params.areaId; if (params.areaName) urlParams.area = params.areaName; if (params.headingId) urlParams['heading-id'] = params.headingId; if (params.headingName) urlParams.heading = params.headingName; if (params.completed) urlParams.completed = params.completed; if (params.canceled) urlParams.canceled = params.canceled; if (params.creationDate) urlParams['creation-date'] = params.creationDate; if (params.completionDate) urlParams['completion-date'] = params.completionDate; const url = buildThingsUrl('add', urlParams); logger.debug('Generated URL', { url }); await openThingsUrl(url); return { content: [{ type: "text", text: `Successfully created to-do: ${params.title}` }] }; } catch (error) { logger.error('Failed to add to-do', { error: error instanceof Error ? error.message : error }); throw error; } }
- src/tools/add-todo.ts:6-59 (schema)Zod schema defining the structure, validation rules, and descriptions for all input parameters of the 'add_todo' tool.const addTodoSchema = z.object({ title: z.string().min(1).describe('To-do title (required). Clear, actionable description of the task'), notes: z.string().max(10000).optional().describe('Additional notes or details for the to-do (max 10,000 characters). Supports markdown formatting for rich text'), when: z.enum(['today', 'tomorrow', 'evening', 'anytime', 'someday']) .or(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)) .optional() .describe('Schedule the to-do for a specific time. Use "today" for immediate action, "tomorrow" for next day, "evening" for later today, "anytime" for no specific time, "someday" for future consideration, or ISO date format (YYYY-MM-DD) for specific date'), deadline: z.string() .regex(/^\d{4}-\d{2}-\d{2}$/) .optional() .describe('Set a deadline for the to-do in ISO date format (YYYY-MM-DD). Creates a deadline reminder in Things.app'), tags: z.array(z.string().min(1)) .max(20) .optional() .describe('Array of tag names for organizing and categorizing the to-do (max 20 tags). Tags help with filtering and organization'), checklistItems: z.array(z.string().min(1)) .max(100) .optional() .describe('Array of checklist item descriptions to add as sub-tasks (max 100 items). Each item becomes a checkable sub-task within the to-do'), projectId: z.string() .optional() .describe('ID of the project to add this to-do to. Use this when you know the specific project ID'), projectName: z.string() .optional() .describe('Name of the project to add this to-do to. Things.app will find the project by name and add the to-do there'), areaId: z.string() .optional() .describe('ID of the area of responsibility to assign this to-do to. Use this when you know the specific area ID'), areaName: z.string() .optional() .describe('Name of the area of responsibility to assign this to-do to (e.g., "Work", "Personal", "Health")'), headingId: z.string() .optional() .describe('ID of a specific heading within the target project to organize the to-do under'), headingName: z.string() .optional() .describe('Name of a heading within the target project to organize the to-do under (e.g., "Phase 1", "Research")'), completed: z.boolean() .optional() .default(false) .describe('Mark the to-do as completed immediately upon creation (default: false). Useful for logging already completed tasks'), canceled: z.boolean() .optional() .default(false) .describe('Mark the to-do as canceled immediately upon creation (default: false). Useful for recording tasks that are no longer needed'), creationDate: z.string() .regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/) .optional() .describe('Override the creation date with a specific ISO8601 datetime (YYYY-MM-DDTHH:MM:SS). Useful for importing historical data'), completionDate: z.string() .regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/) .optional() .describe('Set a specific completion date using ISO8601 datetime (YYYY-MM-DDTHH:MM:SS). Only used when completed is true') });
- src/tools/add-todo.ts:61-108 (registration)Registration function that calls server.tool() to register the 'add_todo' tool with the MCP server, providing the tool name, description, input schema, and handler function.export function registerAddTodoTool(server: McpServer): void { server.tool( 'add_todo', 'Create a new to-do item in Things.app. Add notes, tags, checklist items, and assign to projects or areas.', addTodoSchema.shape, async (params) => { try { logger.info('Adding new to-do', { title: params.title }); const urlParams: Record<string, any> = { title: params.title }; // Map schema parameters to Things URL scheme parameters if (params.notes) urlParams.notes = params.notes; if (params.when) urlParams.when = params.when; if (params.deadline) urlParams.deadline = params.deadline; if (params.tags) urlParams.tags = params.tags.join(','); if (params.checklistItems) urlParams['checklist-items'] = params.checklistItems.join(','); if (params.projectId) urlParams['list-id'] = params.projectId; if (params.projectName) urlParams.list = params.projectName; if (params.areaId) urlParams['area-id'] = params.areaId; if (params.areaName) urlParams.area = params.areaName; if (params.headingId) urlParams['heading-id'] = params.headingId; if (params.headingName) urlParams.heading = params.headingName; if (params.completed) urlParams.completed = params.completed; if (params.canceled) urlParams.canceled = params.canceled; if (params.creationDate) urlParams['creation-date'] = params.creationDate; if (params.completionDate) urlParams['completion-date'] = params.completionDate; const url = buildThingsUrl('add', urlParams); logger.debug('Generated URL', { url }); await openThingsUrl(url); return { content: [{ type: "text", text: `Successfully created to-do: ${params.title}` }] }; } catch (error) { logger.error('Failed to add to-do', { error: error instanceof Error ? error.message : error }); throw error; } } ); }
- src/index.ts:21-21 (registration)Main application entry point where registerAddTodoTool is invoked to register the tool with the MCP server instance.registerAddTodoTool(server);
- src/types/things.ts:17-28 (schema)TypeScript interface defining parameters for adding a todo item, matching the Things.app URL scheme keys used in URL construction.export interface AddTodoParams { title: string; notes?: string; when?: WhenValue; deadline?: string; tags?: string; 'checklist-items'?: string; list?: string; heading?: string; completed?: boolean; canceled?: boolean; }