@iflow-mcp/omnifocus-mcp 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/QUERY_TOOL_EXAMPLES.md +298 -0
  2. package/QUERY_TOOL_REFERENCE.md +228 -0
  3. package/README.md +250 -0
  4. package/assets/omnifocus-mcp-logo.png +0 -0
  5. package/cli.cjs +9 -0
  6. package/dist/omnifocustypes.js +48 -0
  7. package/dist/server.js +44 -0
  8. package/dist/tools/definitions/addOmniFocusTask.js +76 -0
  9. package/dist/tools/definitions/addProject.js +61 -0
  10. package/dist/tools/definitions/batchAddItems.js +89 -0
  11. package/dist/tools/definitions/batchRemoveItems.js +74 -0
  12. package/dist/tools/definitions/dumpDatabase.js +259 -0
  13. package/dist/tools/definitions/editItem.js +88 -0
  14. package/dist/tools/definitions/getPerspectiveView.js +107 -0
  15. package/dist/tools/definitions/listPerspectives.js +65 -0
  16. package/dist/tools/definitions/queryOmnifocus.js +190 -0
  17. package/dist/tools/definitions/removeItem.js +80 -0
  18. package/dist/tools/dumpDatabase.js +121 -0
  19. package/dist/tools/dumpDatabaseOptimized.js +192 -0
  20. package/dist/tools/primitives/addOmniFocusTask.js +227 -0
  21. package/dist/tools/primitives/addProject.js +132 -0
  22. package/dist/tools/primitives/batchAddItems.js +166 -0
  23. package/dist/tools/primitives/batchRemoveItems.js +44 -0
  24. package/dist/tools/primitives/editItem.js +443 -0
  25. package/dist/tools/primitives/getPerspectiveView.js +50 -0
  26. package/dist/tools/primitives/listPerspectives.js +34 -0
  27. package/dist/tools/primitives/queryOmnifocus.js +365 -0
  28. package/dist/tools/primitives/queryOmnifocusDebug.js +135 -0
  29. package/dist/tools/primitives/removeItem.js +177 -0
  30. package/dist/types.js +1 -0
  31. package/dist/utils/cacheManager.js +187 -0
  32. package/dist/utils/dateFormatting.js +58 -0
  33. package/dist/utils/omnifocusScripts/getPerspectiveView.js +169 -0
  34. package/dist/utils/omnifocusScripts/listPerspectives.js +59 -0
  35. package/dist/utils/omnifocusScripts/omnifocusDump.js +223 -0
  36. package/dist/utils/scriptExecution.js +113 -0
  37. package/package.json +37 -0
  38. package/src/omnifocustypes.ts +89 -0
  39. package/src/server.ts +109 -0
  40. package/src/tools/definitions/addOmniFocusTask.ts +80 -0
  41. package/src/tools/definitions/addProject.ts +67 -0
  42. package/src/tools/definitions/batchAddItems.ts +98 -0
  43. package/src/tools/definitions/batchRemoveItems.ts +80 -0
  44. package/src/tools/definitions/dumpDatabase.ts +311 -0
  45. package/src/tools/definitions/editItem.ts +96 -0
  46. package/src/tools/definitions/getPerspectiveView.ts +125 -0
  47. package/src/tools/definitions/listPerspectives.ts +72 -0
  48. package/src/tools/definitions/queryOmnifocus.ts +212 -0
  49. package/src/tools/definitions/removeItem.ts +86 -0
  50. package/src/tools/dumpDatabase.ts +196 -0
  51. package/src/tools/dumpDatabaseOptimized.ts +231 -0
  52. package/src/tools/primitives/addOmniFocusTask.ts +252 -0
  53. package/src/tools/primitives/addProject.ts +156 -0
  54. package/src/tools/primitives/batchAddItems.ts +207 -0
  55. package/src/tools/primitives/batchRemoveItems.ts +64 -0
  56. package/src/tools/primitives/editItem.ts +507 -0
  57. package/src/tools/primitives/getPerspectiveView.ts +71 -0
  58. package/src/tools/primitives/listPerspectives.ts +53 -0
  59. package/src/tools/primitives/queryOmnifocus.ts +394 -0
  60. package/src/tools/primitives/queryOmnifocusDebug.ts +139 -0
  61. package/src/tools/primitives/removeItem.ts +195 -0
  62. package/src/types.ts +107 -0
  63. package/src/utils/cacheManager.ts +234 -0
  64. package/src/utils/dateFormatting.ts +81 -0
  65. package/src/utils/omnifocusScripts/getPerspectiveView.js +169 -0
  66. package/src/utils/omnifocusScripts/listPerspectives.js +59 -0
  67. package/src/utils/omnifocusScripts/omnifocusDump.js +223 -0
  68. package/src/utils/scriptExecution.ts +128 -0
  69. package/tsconfig.json +15 -0
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # OmniFocus MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that integrates with OmniFocus to enable Claude (or other MCP-compatible clients) to interact with your tasks and projects.
4
+
5
+ ![OmniFocus MCP](assets/omnifocus-mcp-logo.png)
6
+
7
+ ## Overview
8
+
9
+ This MCP server creates a bridge between AI assistants (like Claude) and your OmniFocus task management system. It gives AI models the ability to view, create, edit, and remove tasks and projects in your OmniFocus database through natural language conversations.
10
+ Some ways you could use it:
11
+
12
+ - Translate the PDF of a syllabus into a fully specificed project with tasks, tags, defer dates, and due dates.
13
+ - Turn a meeting transcript into a list of actions
14
+ - Create visualizations of your tasks, projects, and tags
15
+ - Process multiple tasks or projects in a single operation
16
+ - Bulk manage your OmniFocus items efficiently
17
+
18
+ **Known Issues**
19
+ - Dump_database tool currently fails for very large omnifocus databases.
20
+
21
+ ## Roadmap
22
+ - ~~Enable the client to interact with perspectives~~ ✅ (Added list_perspectives and get_perspective_view)
23
+ - Benefit from MCP `resource` and `prompt` features
24
+ - Add support for the new `planned` date type in Omnifocus 4.7
25
+ - Support manipulating notifications for projects and tasks
26
+
27
+
28
+ ## 🚀 Quick Start
29
+
30
+ ### Prerequisites
31
+ - macOS with OmniFocus installed
32
+
33
+ ### Connecting to Claude
34
+
35
+ 1. In Claude Desktop, add this MCP server to your configuration file at:
36
+ ```
37
+ ~/Library/Application Support/Claude/claude_desktop_config.json
38
+ ```
39
+
40
+ 2. Add the following configuration:
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "omnifocus": {
45
+ "command": "npx",
46
+ "args": ["-y", "omnifocus-mcp"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ 3. Restart Claude Desktop
53
+
54
+ ## Use Cases
55
+
56
+ ### Efficient Task Queries
57
+ Use the new `query_omnifocus` tool for fast, targeted searches:
58
+ > "Show me tasks due today"
59
+ > "Get all flagged items in my Work project"
60
+ > "Count how many tasks are in each project"
61
+
62
+ ### Reorganize your projects, tasks, and tags
63
+ > "I want every task to have an energy level tag. Show me a list of all the tasks that don't have an energy level tag and your suggestions for what tag to add. I'll make any changes I think are appropriate. Then make the changes in OmniFocus."
64
+
65
+ ### Add tasks from any conversation
66
+
67
+ > "Ok, thanks for the detailed explanation of why the rule of law is important. Add a recurring task to my activism project that reminds me to call my representative weekly. Include a summary of this conversation in the notes field."
68
+
69
+ ### Quick, Virtual Perspectives
70
+
71
+ Get a summary of your current tasks and manage them conversationally:
72
+
73
+ > "Show me all my flagged tasks due this week"
74
+
75
+ Or create custom views:
76
+
77
+ > "What are my next actions in the Work folder?"
78
+
79
+ ### Work with OmniFocus Perspectives
80
+
81
+ List and view your perspectives:
82
+
83
+ > "What perspectives do I have available?"
84
+ > "Show me what's in my Inbox perspective"
85
+ > "Get the flagged items from my current perspective"
86
+
87
+ ### Process Transcripts or PDFs
88
+
89
+ Extract action items from meeting transcripts, academic research articles, or notes:
90
+
91
+ > "I'm pasting in the transcript from today's meeting. Please analyze it and create tasks in OmniFocus for any action items assigned to me. Put them in my 'Product Development' project."
92
+
93
+
94
+ ## 🔧 Available Tools
95
+
96
+ The server currently provides these tools:
97
+
98
+ ### `query_omnifocus` ⭐ NEW
99
+ Efficiently query your OmniFocus database with powerful filters. Get specific tasks, projects, or folders without loading the entire database.
100
+
101
+ Key Features:
102
+ - **Filter by multiple criteria**: project, tags, status, due dates, flags, and more
103
+ - **Request specific fields**: Reduce response size by only getting the data you need
104
+ - **Sort and limit results**: Control the output format
105
+ - **Much faster than dump_database** for targeted queries
106
+
107
+ Common Uses:
108
+ ```
109
+ "Show me all flagged tasks due this week"
110
+ "Get next actions from my Work project"
111
+ "Count tasks in each project" (use with summary: true)
112
+ "Find all tasks deferred until tomorrow"
113
+ ```
114
+
115
+ Parameters:
116
+ - `entity`: Type to query ('tasks', 'projects', or 'folders')
117
+ - `filters`: (Optional) Narrow results by project, tags, status, dates, etc.
118
+ - `fields`: (Optional) Specific fields to return (id, name, dueDate, etc.)
119
+ - `limit`: (Optional) Maximum items to return
120
+ - `sortBy`: (Optional) Field to sort by
121
+ - `includeCompleted`: (Optional) Include completed items (default: false)
122
+ - `summary`: (Optional) Return only count instead of full details
123
+
124
+ ### `dump_database`
125
+ Gets the complete current state of your OmniFocus database. Best for comprehensive analysis or when you need everything.
126
+
127
+ Parameters:
128
+ - `hideCompleted`: (Optional) Hide completed/dropped tasks (default: true)
129
+ - `hideRecurringDuplicates`: (Optional) Hide duplicate recurring tasks (default: true)
130
+
131
+ ### `add_omnifocus_task`
132
+ Add a new task to OmniFocus.
133
+
134
+ Parameters:
135
+ - `name`: The name of the task
136
+ - `projectName`: (Optional) The name of the project to add the task to
137
+ - `note`: (Optional) Additional notes for the task
138
+ - `dueDate`: (Optional) The due date of the task in ISO format
139
+ - `deferDate`: (Optional) The defer date of the task in ISO format
140
+ - `flagged`: (Optional) Whether the task is flagged or not
141
+ - `estimatedMinutes`: (Optional) Estimated time to complete the task
142
+ - `tags`: (Optional) Tags to assign to the task
143
+ - `parentTaskId`: (Optional) Create under an existing parent task by ID
144
+ - `parentTaskName`: (Optional) Create under first matching parent task by name (fallback)
145
+
146
+ ### `add_project`
147
+ Add a new project to OmniFocus.
148
+
149
+ Parameters:
150
+ - `name`: The name of the project
151
+ - `folderName`: (Optional) The name of the folder to add the project to
152
+ - `note`: (Optional) Additional notes for the project
153
+ - `dueDate`: (Optional) The due date of the project in ISO format
154
+ - `deferDate`: (Optional) The defer date of the project in ISO format
155
+ - `flagged`: (Optional) Whether the project is flagged or not
156
+ - `estimatedMinutes`: (Optional) Estimated time to complete the project
157
+ - `tags`: (Optional) Tags to assign to the project
158
+ - `sequential`: (Optional) Whether tasks in the project should be sequential
159
+
160
+ ### `remove_item`
161
+ Remove a task or project from OmniFocus.
162
+
163
+ Parameters:
164
+ - `id`: (Optional) The ID of the task or project to remove
165
+ - `name`: (Optional) The name of the task or project to remove
166
+ - `itemType`: The type of item to remove ('task' or 'project')
167
+
168
+ ### `edit_item`
169
+ Edit a task or project in OmniFocus.
170
+
171
+ Parameters:
172
+ - `id`: (Optional) The ID of the task or project to edit
173
+ - `name`: (Optional) The name of the task or project to edit
174
+ - `itemType`: The type of item to edit ('task' or 'project')
175
+ - Various parameters for editing properties
176
+
177
+ ### `batch_add_items`
178
+ Add multiple tasks or projects to OmniFocus in a single operation.
179
+
180
+ Parameters:
181
+ - `items`: Array of items to add, where each item can be:
182
+ - `type`: The type of item ('task' or 'project')
183
+ - `name`: The name of the item
184
+ - `note`: (Optional) Additional notes
185
+ - `dueDate`: (Optional) Due date in ISO format
186
+ - `deferDate`: (Optional) Defer date in ISO format
187
+ - `flagged`: (Optional) Whether the item is flagged
188
+ - `estimatedMinutes`: (Optional) Estimated completion time
189
+ - `tags`: (Optional) Array of tags
190
+ - `projectName`: (Optional) For tasks: the project to add to
191
+ - `folderName`: (Optional) For projects: the folder to add to
192
+ - `sequential`: (Optional) For projects: whether tasks are sequential
193
+ - `parentTaskId`: (Optional, tasks): Parent task by ID
194
+ - `parentTaskName`: (Optional, tasks): Parent task by name (fallback)
195
+ - `tempId`: (Optional, tasks): Temporary ID for within-batch references
196
+ - `parentTempId`: (Optional, tasks): Reference to another item's `tempId` to establish hierarchy
197
+ - `hierarchyLevel`: (Optional, tasks): Ordering hint (0 for root, 1 for child, ...)
198
+
199
+ Examples:
200
+ ```
201
+ {
202
+ "items": [
203
+ { "type": "task", "name": "Parent", "projectName": "My Project", "tempId": "p1" },
204
+ { "type": "task", "name": "Child A", "parentTempId": "p1" },
205
+ { "type": "task", "name": "Child B", "parentTempId": "p1" }
206
+ ]
207
+ }
208
+ ```
209
+
210
+ ### `batch_remove_items`
211
+ Remove multiple tasks or projects from OmniFocus in a single operation.
212
+
213
+ Parameters:
214
+ - `items`: Array of items to remove, where each item can be:
215
+ - `id`: (Optional) The ID of the item to remove
216
+ - `name`: (Optional) The name of the item to remove
217
+ - `itemType`: The type of item ('task' or 'project')
218
+
219
+ ### `list_perspectives` ⭐ NEW
220
+ List all available perspectives in OmniFocus, including built-in and custom perspectives.
221
+
222
+ Parameters:
223
+ - `includeBuiltIn`: (Optional) Include built-in perspectives like Inbox, Projects, Tags (default: true)
224
+ - `includeCustom`: (Optional) Include custom perspectives (Pro feature) (default: true)
225
+
226
+ Returns:
227
+ - List of perspectives with their names, types (builtin/custom), and whether they can be modified
228
+
229
+ ### `get_perspective_view` ⭐ NEW
230
+ Get the items visible in the current OmniFocus perspective. Shows what tasks and projects are displayed.
231
+
232
+ Parameters:
233
+ - `perspectiveName`: Name of the perspective to view (e.g., 'Inbox', 'Projects', 'Flagged')
234
+ - `limit`: (Optional) Maximum number of items to return (default: 100)
235
+ - `includeMetadata`: (Optional) Include additional metadata like tags and dates (default: true)
236
+ - `fields`: (Optional) Specific fields to include in the response
237
+
238
+ Note: This tool returns the content of the current perspective window. Due to OmniJS limitations, it cannot programmatically switch perspectives.
239
+
240
+ ## Development
241
+
242
+ Documentation to follow.
243
+
244
+ ## How It Works
245
+
246
+ This server uses AppleScript to communicate with OmniFocus, allowing it to interact with the application's native functionality. The server is built using the Model Context Protocol SDK, which provides a standardized way for AI models to interact with external tools and systems.
247
+
248
+ ## 🤝 Contributing
249
+
250
+ Contributions are welcome! Please feel free to submit a Pull Request.
Binary file
package/cli.cjs ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ // cli.cjs
3
+ const path = require('path');
4
+ const childProcess = require('child_process');
5
+
6
+ const serverPath = path.join(__dirname, 'dist', 'server.js');
7
+ childProcess.spawn('node', ['--experimental-modules', serverPath], {
8
+ stdio: 'inherit'
9
+ });
@@ -0,0 +1,48 @@
1
+ // Core enums
2
+ export var Task;
3
+ (function (Task) {
4
+ let Status;
5
+ (function (Status) {
6
+ Status[Status["Available"] = 0] = "Available";
7
+ Status[Status["Blocked"] = 1] = "Blocked";
8
+ Status[Status["Completed"] = 2] = "Completed";
9
+ Status[Status["Dropped"] = 3] = "Dropped";
10
+ Status[Status["DueSoon"] = 4] = "DueSoon";
11
+ Status[Status["Next"] = 5] = "Next";
12
+ Status[Status["Overdue"] = 6] = "Overdue";
13
+ })(Status = Task.Status || (Task.Status = {}));
14
+ let RepetitionMethod;
15
+ (function (RepetitionMethod) {
16
+ RepetitionMethod[RepetitionMethod["DeferUntilDate"] = 0] = "DeferUntilDate";
17
+ RepetitionMethod[RepetitionMethod["DueDate"] = 1] = "DueDate";
18
+ RepetitionMethod[RepetitionMethod["Fixed"] = 2] = "Fixed";
19
+ RepetitionMethod[RepetitionMethod["None"] = 3] = "None";
20
+ })(RepetitionMethod = Task.RepetitionMethod || (Task.RepetitionMethod = {}));
21
+ })(Task || (Task = {}));
22
+ export var Project;
23
+ (function (Project) {
24
+ let Status;
25
+ (function (Status) {
26
+ Status[Status["Active"] = 0] = "Active";
27
+ Status[Status["Done"] = 1] = "Done";
28
+ Status[Status["Dropped"] = 2] = "Dropped";
29
+ Status[Status["OnHold"] = 3] = "OnHold";
30
+ })(Status = Project.Status || (Project.Status = {}));
31
+ })(Project || (Project = {}));
32
+ export var Folder;
33
+ (function (Folder) {
34
+ let Status;
35
+ (function (Status) {
36
+ Status[Status["Active"] = 0] = "Active";
37
+ Status[Status["Dropped"] = 1] = "Dropped";
38
+ })(Status = Folder.Status || (Folder.Status = {}));
39
+ })(Folder || (Folder = {}));
40
+ export var Tag;
41
+ (function (Tag) {
42
+ let Status;
43
+ (function (Status) {
44
+ Status[Status["Active"] = 0] = "Active";
45
+ Status[Status["Dropped"] = 1] = "Dropped";
46
+ Status[Status["OnHold"] = 2] = "OnHold";
47
+ })(Status = Tag.Status || (Tag.Status = {}));
48
+ })(Tag || (Tag = {}));
package/dist/server.js ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ // Import tool definitions
5
+ import * as dumpDatabaseTool from './tools/definitions/dumpDatabase.js';
6
+ import * as addOmniFocusTaskTool from './tools/definitions/addOmniFocusTask.js';
7
+ import * as addProjectTool from './tools/definitions/addProject.js';
8
+ import * as removeItemTool from './tools/definitions/removeItem.js';
9
+ import * as editItemTool from './tools/definitions/editItem.js';
10
+ import * as batchAddItemsTool from './tools/definitions/batchAddItems.js';
11
+ import * as batchRemoveItemsTool from './tools/definitions/batchRemoveItems.js';
12
+ import * as queryOmniFocusTool from './tools/definitions/queryOmnifocus.js';
13
+ import * as listPerspectivesTool from './tools/definitions/listPerspectives.js';
14
+ import * as getPerspectiveViewTool from './tools/definitions/getPerspectiveView.js';
15
+ // Create an MCP server
16
+ const server = new McpServer({
17
+ name: "OmniFocus MCP",
18
+ version: "1.0.0"
19
+ });
20
+ // Register tools
21
+ server.tool("dump_database", "Gets the current state of your OmniFocus database", dumpDatabaseTool.schema.shape, dumpDatabaseTool.handler);
22
+ server.tool("add_omnifocus_task", "Add a new task to OmniFocus", addOmniFocusTaskTool.schema.shape, addOmniFocusTaskTool.handler);
23
+ server.tool("add_project", "Add a new project to OmniFocus", addProjectTool.schema.shape, addProjectTool.handler);
24
+ server.tool("remove_item", "Remove a task or project from OmniFocus", removeItemTool.schema.shape, removeItemTool.handler);
25
+ server.tool("edit_item", "Edit a task or project in OmniFocus", editItemTool.schema.shape, editItemTool.handler);
26
+ server.tool("batch_add_items", "Add multiple tasks or projects to OmniFocus in a single operation", batchAddItemsTool.schema.shape, batchAddItemsTool.handler);
27
+ server.tool("batch_remove_items", "Remove multiple tasks or projects from OmniFocus in a single operation", batchRemoveItemsTool.schema.shape, batchRemoveItemsTool.handler);
28
+ server.tool("query_omnifocus", "Efficiently query OmniFocus database with powerful filters. Get specific tasks, projects, or folders without loading the entire database. Supports filtering by project, tags, status, due dates, and more. Much faster than dump_database for targeted queries.", queryOmniFocusTool.schema.shape, queryOmniFocusTool.handler);
29
+ server.tool("list_perspectives", "List all available perspectives in OmniFocus, including built-in perspectives (Inbox, Projects, Tags, etc.) and custom perspectives (Pro feature)", listPerspectivesTool.schema.shape, listPerspectivesTool.handler);
30
+ server.tool("get_perspective_view", "Get the items visible in a specific OmniFocus perspective. Shows what tasks and projects are displayed when viewing that perspective", getPerspectiveViewTool.schema.shape, getPerspectiveViewTool.handler);
31
+ // Start the MCP server
32
+ const transport = new StdioServerTransport();
33
+ // Use await with server.connect to ensure proper connection
34
+ (async function () {
35
+ try {
36
+ console.error("Starting MCP server...");
37
+ await server.connect(transport);
38
+ console.error("MCP Server connected and ready to accept commands from Claude");
39
+ }
40
+ catch (err) {
41
+ console.error(`Failed to start MCP server: ${err}`);
42
+ }
43
+ })();
44
+ // For a cleaner shutdown if the process is terminated
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ import { addOmniFocusTask } from '../primitives/addOmniFocusTask.js';
3
+ export const schema = z.object({
4
+ name: z.string().describe("The name of the task"),
5
+ note: z.string().optional().describe("Additional notes for the task"),
6
+ dueDate: z.string().optional().describe("The due date of the task in ISO format (YYYY-MM-DD or full ISO date)"),
7
+ deferDate: z.string().optional().describe("The defer date of the task in ISO format (YYYY-MM-DD or full ISO date)"),
8
+ flagged: z.boolean().optional().describe("Whether the task is flagged or not"),
9
+ estimatedMinutes: z.number().optional().describe("Estimated time to complete the task, in minutes"),
10
+ tags: z.array(z.string()).optional().describe("Tags to assign to the task"),
11
+ projectName: z.string().optional().describe("The name of the project to add the task to (will add to inbox if not specified)"),
12
+ // Hierarchy support
13
+ parentTaskId: z.string().optional().describe("ID of the parent task (preferred for accuracy)"),
14
+ parentTaskName: z.string().optional().describe("Name of the parent task (used if ID not provided; matched within project or globally if no project)"),
15
+ hierarchyLevel: z.number().int().min(0).optional().describe("Explicit level indicator for ordering in batch workflows (0=root) - ignored in single add")
16
+ });
17
+ export async function handler(args, extra) {
18
+ try {
19
+ // Call the addOmniFocusTask function
20
+ const result = await addOmniFocusTask(args);
21
+ console.error('[add_omnifocus_task] args:', JSON.stringify(args));
22
+ console.error('[add_omnifocus_task] result:', JSON.stringify(result));
23
+ if (result.success) {
24
+ // Determine actual placement
25
+ const placement = result.placement;
26
+ let locationText = '';
27
+ if (placement === 'parent') {
28
+ locationText = 'under the parent task';
29
+ }
30
+ else if (placement === 'project') {
31
+ locationText = args.projectName ? `in project "${args.projectName}"` : 'in a project';
32
+ }
33
+ else {
34
+ locationText = 'in your inbox';
35
+ }
36
+ const tagText = args.tags && args.tags.length > 0
37
+ ? ` with tags: ${args.tags.join(', ')}`
38
+ : '';
39
+ const dueDateText = args.dueDate
40
+ ? ` due on ${new Date(args.dueDate).toLocaleDateString()}`
41
+ : '';
42
+ // Warning if parent requested but not used
43
+ let placementWarning = '';
44
+ if ((args.parentTaskId || args.parentTaskName) && placement && placement !== 'parent') {
45
+ placementWarning = `\n⚠️ Parent not found; task created ${placement === 'project' ? 'in project' : 'in inbox'}.`;
46
+ }
47
+ return {
48
+ content: [{
49
+ type: "text",
50
+ text: `✅ Task "${args.name}" created successfully ${locationText}${dueDateText}${tagText}.${placementWarning}`
51
+ }]
52
+ };
53
+ }
54
+ else {
55
+ // Task creation failed
56
+ return {
57
+ content: [{
58
+ type: "text",
59
+ text: `Failed to create task: ${result.error}`
60
+ }],
61
+ isError: true
62
+ };
63
+ }
64
+ }
65
+ catch (err) {
66
+ const error = err;
67
+ console.error(`Tool execution error: ${error.message}`);
68
+ return {
69
+ content: [{
70
+ type: "text",
71
+ text: `Error creating task: ${error.message}`
72
+ }],
73
+ isError: true
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ import { addProject } from '../primitives/addProject.js';
3
+ export const schema = z.object({
4
+ name: z.string().describe("The name of the project"),
5
+ note: z.string().optional().describe("Additional notes for the project"),
6
+ dueDate: z.string().optional().describe("The due date of the project in ISO format (YYYY-MM-DD or full ISO date)"),
7
+ deferDate: z.string().optional().describe("The defer date of the project in ISO format (YYYY-MM-DD or full ISO date)"),
8
+ flagged: z.boolean().optional().describe("Whether the project is flagged or not"),
9
+ estimatedMinutes: z.number().optional().describe("Estimated time to complete the project, in minutes"),
10
+ tags: z.array(z.string()).optional().describe("Tags to assign to the project"),
11
+ folderName: z.string().optional().describe("The name of the folder to add the project to (will add to root if not specified)"),
12
+ sequential: z.boolean().optional().describe("Whether tasks in the project should be sequential (default: false)")
13
+ });
14
+ export async function handler(args, extra) {
15
+ try {
16
+ // Call the addProject function
17
+ const result = await addProject(args);
18
+ if (result.success) {
19
+ // Project was added successfully
20
+ let locationText = args.folderName
21
+ ? `in folder "${args.folderName}"`
22
+ : "at the root level";
23
+ let tagText = args.tags && args.tags.length > 0
24
+ ? ` with tags: ${args.tags.join(', ')}`
25
+ : "";
26
+ let dueDateText = args.dueDate
27
+ ? ` due on ${new Date(args.dueDate).toLocaleDateString()}`
28
+ : "";
29
+ let sequentialText = args.sequential
30
+ ? " (sequential)"
31
+ : " (parallel)";
32
+ return {
33
+ content: [{
34
+ type: "text",
35
+ text: `✅ Project "${args.name}" created successfully ${locationText}${dueDateText}${tagText}${sequentialText}.`
36
+ }]
37
+ };
38
+ }
39
+ else {
40
+ // Project creation failed
41
+ return {
42
+ content: [{
43
+ type: "text",
44
+ text: `Failed to create project: ${result.error}`
45
+ }],
46
+ isError: true
47
+ };
48
+ }
49
+ }
50
+ catch (err) {
51
+ const error = err;
52
+ console.error(`Tool execution error: ${error.message}`);
53
+ return {
54
+ content: [{
55
+ type: "text",
56
+ text: `Error creating project: ${error.message}`
57
+ }],
58
+ isError: true
59
+ };
60
+ }
61
+ }
@@ -0,0 +1,89 @@
1
+ import { z } from 'zod';
2
+ import { batchAddItems } from '../primitives/batchAddItems.js';
3
+ export const schema = z.object({
4
+ items: z.array(z.object({
5
+ type: z.enum(['task', 'project']).describe("Type of item to add ('task' or 'project')"),
6
+ name: z.string().describe("The name of the item"),
7
+ note: z.string().optional().describe("Additional notes for the item"),
8
+ dueDate: z.string().optional().describe("The due date in ISO format (YYYY-MM-DD or full ISO date)"),
9
+ deferDate: z.string().optional().describe("The defer date in ISO format (YYYY-MM-DD or full ISO date)"),
10
+ flagged: z.boolean().optional().describe("Whether the item is flagged or not"),
11
+ estimatedMinutes: z.number().optional().describe("Estimated time to complete the item, in minutes"),
12
+ tags: z.array(z.string()).optional().describe("Tags to assign to the item"),
13
+ // Task-specific properties
14
+ projectName: z.string().optional().describe("For tasks: The name of the project to add the task to"),
15
+ parentTaskId: z.string().optional().describe("For tasks: ID of the parent task"),
16
+ parentTaskName: z.string().optional().describe("For tasks: Name of the parent task (scoped to project when provided)"),
17
+ tempId: z.string().optional().describe("For tasks: Temporary ID for within-batch references"),
18
+ parentTempId: z.string().optional().describe("For tasks: Reference to parent's tempId within the batch"),
19
+ hierarchyLevel: z.number().int().min(0).optional().describe("Optional ordering hint (0=root, 1=child, ...)"),
20
+ // Project-specific properties
21
+ folderName: z.string().optional().describe("For projects: The name of the folder to add the project to"),
22
+ sequential: z.boolean().optional().describe("For projects: Whether tasks in the project should be sequential")
23
+ })).describe("Array of items (tasks or projects) to add"),
24
+ createSequentially: z.boolean().optional().describe("Process parents before children; when false, best-effort order will still try to resolve parents first")
25
+ });
26
+ export async function handler(args, extra) {
27
+ try {
28
+ // Call the batchAddItems function
29
+ const result = await batchAddItems(args.items);
30
+ if (result.success) {
31
+ const successCount = result.results.filter(r => r.success).length;
32
+ const failureCount = result.results.filter(r => !r.success).length;
33
+ let message = `✅ Successfully added ${successCount} items.`;
34
+ if (failureCount > 0) {
35
+ message += ` ⚠️ Failed to add ${failureCount} items.`;
36
+ }
37
+ // Include details about added items
38
+ const details = result.results.map((item, index) => {
39
+ if (item.success) {
40
+ const itemType = args.items[index].type;
41
+ const itemName = args.items[index].name;
42
+ return `- ✅ ${itemType}: "${itemName}"`;
43
+ }
44
+ else {
45
+ const itemType = args.items[index].type;
46
+ const itemName = args.items[index].name;
47
+ return `- ❌ ${itemType}: "${itemName}" - Error: ${item.error}`;
48
+ }
49
+ }).join('\n');
50
+ return {
51
+ content: [{
52
+ type: "text",
53
+ text: `${message}\n\n${details}`
54
+ }]
55
+ };
56
+ }
57
+ else {
58
+ console.error('[batch_add_items] failure result:', JSON.stringify(result));
59
+ // Batch operation failed completely or no items succeeded.
60
+ const failureDetails = (result.results && result.results.length > 0)
61
+ ? result.results.map((r, index) => {
62
+ const itemType = args.items[index].type;
63
+ const itemName = args.items[index].name;
64
+ return r.success
65
+ ? `- ✅ ${itemType}: \"${itemName}\"`
66
+ : `- ❌ ${itemType}: \"${itemName}\" - Error: ${r?.error || 'Unknown error'}`;
67
+ }).join('\\n')
68
+ : `No items processed. ${result.error || ''}`;
69
+ return {
70
+ content: [{
71
+ type: "text",
72
+ text: `Failed to process batch operation.\\n\\n${failureDetails}`
73
+ }],
74
+ isError: true
75
+ };
76
+ }
77
+ }
78
+ catch (err) {
79
+ const error = err;
80
+ console.error(`Tool execution error: ${error.message}`);
81
+ return {
82
+ content: [{
83
+ type: "text",
84
+ text: `Error processing batch operation: ${error.message}`
85
+ }],
86
+ isError: true
87
+ };
88
+ }
89
+ }
@@ -0,0 +1,74 @@
1
+ import { z } from 'zod';
2
+ import { batchRemoveItems } from '../primitives/batchRemoveItems.js';
3
+ export const schema = z.object({
4
+ items: z.array(z.object({
5
+ id: z.string().optional().describe("The ID of the task or project to remove"),
6
+ name: z.string().optional().describe("The name of the task or project to remove (as fallback if ID not provided)"),
7
+ itemType: z.enum(['task', 'project']).describe("Type of item to remove ('task' or 'project')")
8
+ })).describe("Array of items (tasks or projects) to remove")
9
+ });
10
+ export async function handler(args, extra) {
11
+ try {
12
+ // Validate that each item has at least an ID or name
13
+ for (const item of args.items) {
14
+ if (!item.id && !item.name) {
15
+ return {
16
+ content: [{
17
+ type: "text",
18
+ text: "Each item must have either id or name provided to remove it."
19
+ }],
20
+ isError: true
21
+ };
22
+ }
23
+ }
24
+ // Call the batchRemoveItems function
25
+ const result = await batchRemoveItems(args.items);
26
+ if (result.success) {
27
+ const successCount = result.results.filter(r => r.success).length;
28
+ const failureCount = result.results.filter(r => !r.success).length;
29
+ let message = `✅ Successfully removed ${successCount} items.`;
30
+ if (failureCount > 0) {
31
+ message += ` ⚠️ Failed to remove ${failureCount} items.`;
32
+ }
33
+ // Include details about removed items
34
+ const details = result.results.map((item, index) => {
35
+ if (item.success) {
36
+ const itemType = args.items[index].itemType;
37
+ return `- ✅ ${itemType}: "${item.name}"`;
38
+ }
39
+ else {
40
+ const itemType = args.items[index].itemType;
41
+ const identifier = args.items[index].id || args.items[index].name;
42
+ return `- ❌ ${itemType}: ${identifier} - Error: ${item.error}`;
43
+ }
44
+ }).join('\n');
45
+ return {
46
+ content: [{
47
+ type: "text",
48
+ text: `${message}\n\n${details}`
49
+ }]
50
+ };
51
+ }
52
+ else {
53
+ // Batch operation failed completely
54
+ return {
55
+ content: [{
56
+ type: "text",
57
+ text: `Failed to process batch removal: ${result.error}`
58
+ }],
59
+ isError: true
60
+ };
61
+ }
62
+ }
63
+ catch (err) {
64
+ const error = err;
65
+ console.error(`Tool execution error: ${error.message}`);
66
+ return {
67
+ content: [{
68
+ type: "text",
69
+ text: `Error processing batch removal: ${error.message}`
70
+ }],
71
+ isError: true
72
+ };
73
+ }
74
+ }