omnifocus-mcp-enhanced 1.1.0

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.
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ import { addSubtask } from '../primitives/addSubtask.js';
3
+ export const schema = z.object({
4
+ name: z.string().describe("The name of the subtask"),
5
+ parentTaskId: z.string().optional().describe("The ID of the parent task"),
6
+ parentTaskName: z.string().optional().describe("The name of the parent task (alternative to parentTaskId)"),
7
+ note: z.string().optional().describe("Additional notes for the subtask"),
8
+ dueDate: z.string().optional().describe("The due date of the subtask in ISO format (YYYY-MM-DD or full ISO date)"),
9
+ deferDate: z.string().optional().describe("The defer date of the subtask in ISO format (YYYY-MM-DD or full ISO date)"),
10
+ flagged: z.boolean().optional().describe("Whether the subtask is flagged or not"),
11
+ estimatedMinutes: z.number().optional().describe("Estimated time to complete the subtask, in minutes"),
12
+ tags: z.array(z.string()).optional().describe("Tags to assign to the subtask")
13
+ });
14
+ export async function handler(args, extra) {
15
+ try {
16
+ // Validate that either parentTaskId or parentTaskName is provided
17
+ if (!args.parentTaskId && !args.parentTaskName) {
18
+ return {
19
+ content: [{
20
+ type: "text",
21
+ text: "Error: Either parentTaskId or parentTaskName must be provided for subtask creation."
22
+ }],
23
+ isError: true
24
+ };
25
+ }
26
+ // Call the addSubtask function
27
+ const result = await addSubtask(args);
28
+ if (result.success) {
29
+ // Subtask was added successfully
30
+ const parentRef = args.parentTaskId || args.parentTaskName;
31
+ let tagText = args.tags && args.tags.length > 0
32
+ ? ` with tags: ${args.tags.join(', ')}`
33
+ : "";
34
+ let dueDateText = args.dueDate
35
+ ? ` due on ${new Date(args.dueDate).toLocaleDateString()}`
36
+ : "";
37
+ return {
38
+ content: [{
39
+ type: "text",
40
+ text: `✅ Subtask "${args.name}" created successfully under parent task "${parentRef}"${dueDateText}${tagText}.`
41
+ }]
42
+ };
43
+ }
44
+ else {
45
+ // Subtask creation failed
46
+ return {
47
+ content: [{
48
+ type: "text",
49
+ text: `Failed to create subtask: ${result.error}`
50
+ }],
51
+ isError: true
52
+ };
53
+ }
54
+ }
55
+ catch (err) {
56
+ const error = err;
57
+ console.error(`Tool execution error: ${error.message}`);
58
+ return {
59
+ content: [{
60
+ type: "text",
61
+ text: `Error creating subtask: ${error.message}`
62
+ }],
63
+ isError: true
64
+ };
65
+ }
66
+ }
@@ -0,0 +1,75 @@
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: The ID of the parent task to create this task as a subtask"),
16
+ parentTaskName: z.string().optional().describe("For tasks: The name of the parent task to create this task as a subtask"),
17
+ // Project-specific properties
18
+ folderName: z.string().optional().describe("For projects: The name of the folder to add the project to"),
19
+ sequential: z.boolean().optional().describe("For projects: Whether tasks in the project should be sequential")
20
+ })).describe("Array of items (tasks or projects) to add")
21
+ });
22
+ export async function handler(args, extra) {
23
+ try {
24
+ // Call the batchAddItems function
25
+ const result = await batchAddItems(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 added ${successCount} items.`;
30
+ if (failureCount > 0) {
31
+ message += ` ⚠️ Failed to add ${failureCount} items.`;
32
+ }
33
+ // Include details about added items
34
+ const details = result.results.map((item, index) => {
35
+ if (item.success) {
36
+ const itemType = args.items[index].type;
37
+ const itemName = args.items[index].name;
38
+ return `- ✅ ${itemType}: "${itemName}"`;
39
+ }
40
+ else {
41
+ const itemType = args.items[index].type;
42
+ const itemName = args.items[index].name;
43
+ return `- ❌ ${itemType}: "${itemName}" - Error: ${item.error}`;
44
+ }
45
+ }).join('\n');
46
+ return {
47
+ content: [{
48
+ type: "text",
49
+ text: `${message}\n\n${details}`
50
+ }]
51
+ };
52
+ }
53
+ else {
54
+ // Batch operation failed completely
55
+ return {
56
+ content: [{
57
+ type: "text",
58
+ text: `Failed to process batch operation: ${result.error}`
59
+ }],
60
+ isError: true
61
+ };
62
+ }
63
+ }
64
+ catch (err) {
65
+ const error = err;
66
+ console.error(`Tool execution error: ${error.message}`);
67
+ return {
68
+ content: [{
69
+ type: "text",
70
+ text: `Error processing batch operation: ${error.message}`
71
+ }],
72
+ isError: true
73
+ };
74
+ }
75
+ }
@@ -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
+ }
@@ -0,0 +1,249 @@
1
+ import { z } from 'zod';
2
+ import { dumpDatabase } from '../dumpDatabase.js';
3
+ export const schema = z.object({
4
+ hideCompleted: z.boolean().optional().describe("Set to false to show completed and dropped tasks (default: true)"),
5
+ hideRecurringDuplicates: z.boolean().optional().describe("Set to true to hide duplicate instances of recurring tasks (default: true)")
6
+ });
7
+ export async function handler(args, extra) {
8
+ try {
9
+ // Get raw database
10
+ const database = await dumpDatabase();
11
+ // Format as compact report
12
+ const formattedReport = formatCompactReport(database, {
13
+ hideCompleted: args.hideCompleted !== false, // Default to true
14
+ hideRecurringDuplicates: args.hideRecurringDuplicates !== false // Default to true
15
+ });
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: formattedReport
20
+ }]
21
+ };
22
+ }
23
+ catch (err) {
24
+ return {
25
+ content: [{
26
+ type: "text",
27
+ text: `Error generating report. Please ensure OmniFocus is running and try again.`
28
+ }],
29
+ isError: true
30
+ };
31
+ }
32
+ }
33
+ // Function to format date in compact format (M/D)
34
+ function formatCompactDate(isoDate) {
35
+ if (!isoDate)
36
+ return '';
37
+ const date = new Date(isoDate);
38
+ return `${date.getMonth() + 1}/${date.getDate()}`;
39
+ }
40
+ // Function to format the database in the compact report format
41
+ function formatCompactReport(database, options) {
42
+ const { hideCompleted, hideRecurringDuplicates } = options;
43
+ // Get current date for the header
44
+ const today = new Date();
45
+ const dateStr = today.toISOString().split('T')[0];
46
+ let output = `# OMNIFOCUS [${dateStr}]\n\n`;
47
+ // Add legend
48
+ output += `FORMAT LEGEND:
49
+ F: Folder | P: Project | •: Task | 🚩: Flagged
50
+ Dates: [M/D] | Duration: (30m) or (2h) | Tags: <tag1,tag2>
51
+ Status: #next #avail #block #due #over #compl #drop\n\n`;
52
+ // Map of folder IDs to folder objects for quick lookup
53
+ const folderMap = new Map();
54
+ Object.values(database.folders).forEach((folder) => {
55
+ folderMap.set(folder.id, folder);
56
+ });
57
+ // Get all tag names to compute minimum unique prefixes
58
+ const allTagNames = Object.values(database.tags).map((tag) => tag.name);
59
+ const tagPrefixMap = computeMinimumUniquePrefixes(allTagNames);
60
+ // Function to get folder hierarchy path
61
+ function getFolderPath(folderId) {
62
+ const path = [];
63
+ let currentId = folderId;
64
+ while (currentId) {
65
+ const folder = folderMap.get(currentId);
66
+ if (!folder)
67
+ break;
68
+ path.unshift(folder.name);
69
+ currentId = folder.parentFolderID;
70
+ }
71
+ return path;
72
+ }
73
+ // Get root folders (no parent)
74
+ const rootFolders = Object.values(database.folders).filter((folder) => !folder.parentFolderID);
75
+ // Process folders recursively
76
+ function processFolder(folder, level) {
77
+ const indent = ' '.repeat(level);
78
+ let folderOutput = `${indent}F: ${folder.name}\n`;
79
+ // Process subfolders
80
+ if (folder.subfolders && folder.subfolders.length > 0) {
81
+ for (const subfolderId of folder.subfolders) {
82
+ const subfolder = database.folders[subfolderId];
83
+ if (subfolder) {
84
+ folderOutput += `${processFolder(subfolder, level + 1)}`;
85
+ }
86
+ }
87
+ }
88
+ // Process projects in this folder
89
+ if (folder.projects && folder.projects.length > 0) {
90
+ for (const projectId of folder.projects) {
91
+ const project = database.projects[projectId];
92
+ if (project) {
93
+ folderOutput += processProject(project, level + 1);
94
+ }
95
+ }
96
+ }
97
+ return folderOutput;
98
+ }
99
+ // Process a project
100
+ function processProject(project, level) {
101
+ const indent = ' '.repeat(level);
102
+ // Skip if it's completed or dropped and we're hiding completed items
103
+ if (hideCompleted && (project.status === 'Done' || project.status === 'Dropped')) {
104
+ return '';
105
+ }
106
+ // Format project status info
107
+ let statusInfo = '';
108
+ if (project.status === 'OnHold') {
109
+ statusInfo = ' [OnHold]';
110
+ }
111
+ else if (project.status === 'Dropped') {
112
+ statusInfo = ' [Dropped]';
113
+ }
114
+ // Add due date if present
115
+ if (project.dueDate) {
116
+ const dueDateStr = formatCompactDate(project.dueDate);
117
+ statusInfo += statusInfo ? ` [DUE:${dueDateStr}]` : ` [DUE:${dueDateStr}]`;
118
+ }
119
+ // Add flag if present
120
+ const flaggedSymbol = project.flagged ? ' 🚩' : '';
121
+ let projectOutput = `${indent}P: ${project.name}${flaggedSymbol}${statusInfo}\n`;
122
+ // Process tasks in this project
123
+ const projectTasks = database.tasks.filter((task) => task.projectId === project.id && !task.parentId);
124
+ if (projectTasks.length > 0) {
125
+ for (const task of projectTasks) {
126
+ projectOutput += processTask(task, level + 1);
127
+ }
128
+ }
129
+ return projectOutput;
130
+ }
131
+ // Process a task
132
+ function processTask(task, level) {
133
+ const indent = ' '.repeat(level);
134
+ // Skip if it's completed or dropped and we're hiding completed items
135
+ if (hideCompleted && (task.completed || task.taskStatus === 'Completed' || task.taskStatus === 'Dropped')) {
136
+ return '';
137
+ }
138
+ // Flag symbol
139
+ const flagSymbol = task.flagged ? '🚩 ' : '';
140
+ // Format dates
141
+ let dateInfo = '';
142
+ if (task.dueDate) {
143
+ const dueDateStr = formatCompactDate(task.dueDate);
144
+ dateInfo += ` [DUE:${dueDateStr}]`;
145
+ }
146
+ if (task.deferDate) {
147
+ const deferDateStr = formatCompactDate(task.deferDate);
148
+ dateInfo += ` [defer:${deferDateStr}]`;
149
+ }
150
+ // Format duration
151
+ let durationStr = '';
152
+ if (task.estimatedMinutes) {
153
+ // Convert to hours if >= 60 minutes
154
+ if (task.estimatedMinutes >= 60) {
155
+ const hours = Math.floor(task.estimatedMinutes / 60);
156
+ durationStr = ` (${hours}h)`;
157
+ }
158
+ else {
159
+ durationStr = ` (${task.estimatedMinutes}m)`;
160
+ }
161
+ }
162
+ // Format tags
163
+ let tagsStr = '';
164
+ if (task.tagNames && task.tagNames.length > 0) {
165
+ // Use minimum unique prefixes for tag names
166
+ const abbreviatedTags = task.tagNames.map((tag) => {
167
+ return tagPrefixMap.get(tag) || tag;
168
+ });
169
+ tagsStr = ` <${abbreviatedTags.join(',')}>`;
170
+ }
171
+ // Format status
172
+ let statusStr = '';
173
+ switch (task.taskStatus) {
174
+ case 'Next':
175
+ statusStr = ' #next';
176
+ break;
177
+ case 'Available':
178
+ statusStr = ' #avail';
179
+ break;
180
+ case 'Blocked':
181
+ statusStr = ' #block';
182
+ break;
183
+ case 'DueSoon':
184
+ statusStr = ' #due';
185
+ break;
186
+ case 'Overdue':
187
+ statusStr = ' #over';
188
+ break;
189
+ case 'Completed':
190
+ statusStr = ' #compl';
191
+ break;
192
+ case 'Dropped':
193
+ statusStr = ' #drop';
194
+ break;
195
+ }
196
+ let taskOutput = `${indent}• ${flagSymbol}${task.name}${dateInfo}${durationStr}${tagsStr}${statusStr}\n`;
197
+ // Process subtasks
198
+ if (task.childIds && task.childIds.length > 0) {
199
+ const childTasks = database.tasks.filter((t) => task.childIds.includes(t.id));
200
+ for (const childTask of childTasks) {
201
+ taskOutput += processTask(childTask, level + 1);
202
+ }
203
+ }
204
+ return taskOutput;
205
+ }
206
+ // Process all root folders
207
+ for (const folder of rootFolders) {
208
+ output += processFolder(folder, 0);
209
+ }
210
+ // Process projects not in any folder (if any)
211
+ const rootProjects = Object.values(database.projects).filter((project) => !project.folderID);
212
+ for (const project of rootProjects) {
213
+ output += processProject(project, 0);
214
+ }
215
+ return output;
216
+ }
217
+ // Compute minimum unique prefixes for all tags (minimum 3 characters)
218
+ function computeMinimumUniquePrefixes(tagNames) {
219
+ const prefixMap = new Map();
220
+ // For each tag name
221
+ for (const tagName of tagNames) {
222
+ // Start with minimum length of 3
223
+ let prefixLength = 3;
224
+ let isUnique = false;
225
+ // Keep increasing prefix length until we find a unique prefix
226
+ while (!isUnique && prefixLength <= tagName.length) {
227
+ const prefix = tagName.substring(0, prefixLength);
228
+ // Check if this prefix uniquely identifies the tag
229
+ isUnique = tagNames.every(otherTag => {
230
+ // If it's the same tag, skip comparison
231
+ if (otherTag === tagName)
232
+ return true;
233
+ // If the other tag starts with the same prefix, it's not unique
234
+ return !otherTag.startsWith(prefix);
235
+ });
236
+ if (isUnique) {
237
+ prefixMap.set(tagName, prefix);
238
+ }
239
+ else {
240
+ prefixLength++;
241
+ }
242
+ }
243
+ // If we couldn't find a unique prefix, use the full tag name
244
+ if (!isUnique) {
245
+ prefixMap.set(tagName, tagName);
246
+ }
247
+ }
248
+ return prefixMap;
249
+ }
@@ -0,0 +1,88 @@
1
+ import { z } from 'zod';
2
+ import { editItem } from '../primitives/editItem.js';
3
+ export const schema = z.object({
4
+ id: z.string().optional().describe("The ID of the task or project to edit"),
5
+ name: z.string().optional().describe("The name of the task or project to edit (as fallback if ID not provided)"),
6
+ itemType: z.enum(['task', 'project']).describe("Type of item to edit ('task' or 'project')"),
7
+ // Common editable fields
8
+ newName: z.string().optional().describe("New name for the item"),
9
+ newNote: z.string().optional().describe("New note for the item"),
10
+ newDueDate: z.string().optional().describe("New due date in ISO format (YYYY-MM-DD or full ISO date); set to empty string to clear"),
11
+ newDeferDate: z.string().optional().describe("New defer date in ISO format (YYYY-MM-DD or full ISO date); set to empty string to clear"),
12
+ newFlagged: z.boolean().optional().describe("Set flagged status (set to false for no flag, true for flag)"),
13
+ newEstimatedMinutes: z.number().optional().describe("New estimated minutes"),
14
+ // Task-specific fields
15
+ newStatus: z.enum(['incomplete', 'completed', 'dropped']).optional().describe("New status for tasks (incomplete, completed, dropped)"),
16
+ addTags: z.array(z.string()).optional().describe("Tags to add to the task"),
17
+ removeTags: z.array(z.string()).optional().describe("Tags to remove from the task"),
18
+ replaceTags: z.array(z.string()).optional().describe("Tags to replace all existing tags with"),
19
+ // Project-specific fields
20
+ newSequential: z.boolean().optional().describe("Whether the project should be sequential"),
21
+ newFolderName: z.string().optional().describe("New folder to move the project to"),
22
+ newProjectStatus: z.enum(['active', 'completed', 'dropped', 'onHold']).optional().describe("New status for projects")
23
+ });
24
+ export async function handler(args, extra) {
25
+ try {
26
+ // Validate that either id or name is provided
27
+ if (!args.id && !args.name) {
28
+ return {
29
+ content: [{
30
+ type: "text",
31
+ text: "Either id or name must be provided to edit an item."
32
+ }],
33
+ isError: true
34
+ };
35
+ }
36
+ // Call the editItem function
37
+ const result = await editItem(args);
38
+ if (result.success) {
39
+ // Item was edited successfully
40
+ const itemTypeLabel = args.itemType === 'task' ? 'Task' : 'Project';
41
+ let changedText = '';
42
+ if (result.changedProperties) {
43
+ changedText = ` (${result.changedProperties})`;
44
+ }
45
+ return {
46
+ content: [{
47
+ type: "text",
48
+ text: `✅ ${itemTypeLabel} "${result.name}" updated successfully${changedText}.`
49
+ }]
50
+ };
51
+ }
52
+ else {
53
+ // Item editing failed
54
+ let errorMsg = `Failed to update ${args.itemType}`;
55
+ if (result.error) {
56
+ if (result.error.includes("Item not found")) {
57
+ errorMsg = `${args.itemType.charAt(0).toUpperCase() + args.itemType.slice(1)} not found`;
58
+ if (args.id)
59
+ errorMsg += ` with ID "${args.id}"`;
60
+ if (args.name)
61
+ errorMsg += `${args.id ? ' or' : ' with'} name "${args.name}"`;
62
+ errorMsg += '.';
63
+ }
64
+ else {
65
+ errorMsg += `: ${result.error}`;
66
+ }
67
+ }
68
+ return {
69
+ content: [{
70
+ type: "text",
71
+ text: errorMsg
72
+ }],
73
+ isError: true
74
+ };
75
+ }
76
+ }
77
+ catch (err) {
78
+ const error = err;
79
+ console.error(`Tool execution error: ${error.message}`);
80
+ return {
81
+ content: [{
82
+ type: "text",
83
+ text: `Error updating ${args.itemType}: ${error.message}`
84
+ }],
85
+ isError: true
86
+ };
87
+ }
88
+ }
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ import { getTaskById } from '../primitives/getTaskById.js';
3
+ export const schema = z.object({
4
+ taskId: z.string().optional().describe("The ID of the task to retrieve"),
5
+ taskName: z.string().optional().describe("The name of the task to retrieve (alternative to taskId)")
6
+ });
7
+ export async function handler(args, extra) {
8
+ try {
9
+ // Validate that either taskId or taskName is provided
10
+ if (!args.taskId && !args.taskName) {
11
+ return {
12
+ content: [{
13
+ type: "text",
14
+ text: "Error: Either taskId or taskName must be provided."
15
+ }],
16
+ isError: true
17
+ };
18
+ }
19
+ // Call the getTaskById function
20
+ const result = await getTaskById(args);
21
+ if (result.success && result.task) {
22
+ const task = result.task;
23
+ // Format task information for display
24
+ let infoText = `📋 **Task Information**\n`;
25
+ infoText += `• **Name**: ${task.name}\n`;
26
+ infoText += `• **ID**: ${task.id}\n`;
27
+ if (task.note) {
28
+ infoText += `• **Note**: ${task.note}\n`;
29
+ }
30
+ if (task.parentId && task.parentName) {
31
+ infoText += `• **Parent Task**: ${task.parentName} (${task.parentId})\n`;
32
+ }
33
+ if (task.projectId && task.projectName) {
34
+ infoText += `• **Project**: ${task.projectName} (${task.projectId})\n`;
35
+ }
36
+ infoText += `• **Has Children**: ${task.hasChildren ? `Yes (${task.childrenCount} subtasks)` : 'No'}\n`;
37
+ return {
38
+ content: [{
39
+ type: "text",
40
+ text: infoText
41
+ }]
42
+ };
43
+ }
44
+ else {
45
+ // Task retrieval failed
46
+ return {
47
+ content: [{
48
+ type: "text",
49
+ text: `Failed to retrieve task: ${result.error}`
50
+ }],
51
+ isError: true
52
+ };
53
+ }
54
+ }
55
+ catch (err) {
56
+ const error = err;
57
+ console.error(`Tool execution error: ${error.message}`);
58
+ return {
59
+ content: [{
60
+ type: "text",
61
+ text: `Error retrieving task: ${error.message}`
62
+ }],
63
+ isError: true
64
+ };
65
+ }
66
+ }