remove_tasks
Remove tasks from goals by marking them as deleted while keeping them in the system. Specify task IDs and optionally delete child tasks.
Instructions
Soft-delete multiple tasks from a goal. Tasks are marked as deleted but remain in the system. Task IDs use a dot-notation (e.g., "1", "1.1", "1.1.1"). Responses will return simplified task objects without createdAt, updatedAt, or parentId. Soft-deleted tasks are excluded by default from get_tasks results unless includeDeletedTasks is set to true.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| goalId | Yes | ID of the goal to remove tasks from (number) | |
| taskIds | Yes | IDs of the tasks to remove (array of strings). Example: ["1", "1.1"]. | |
| deleteChildren | No | Whether to delete child tasks along with the parent (boolean). Defaults to false. If false, attempting to delete a parent task with existing subtasks will throw an error. |
Implementation Reference
- src/storage.ts:236-334 (handler)Core handler function that soft-deletes the specified tasks (and optionally their children), collects removed tasks, updates parent completion statuses, and returns results.async removeTasks( goalId: number, taskIds: string[], deleteChildren: boolean = false // New parameter ): Promise<{ removedTasks: TaskResponse[]; completedParents: TaskResponse[] }> { const plan = await this.getPlan(goalId); if (!plan) { throw new Error(`No plan found for goal ${goalId}`); } const removedTasks: TaskResponse[] = []; const completedParents: TaskResponse[] = []; const parentsToCheck: Set<string | null> = new Set(); // Sort taskIds to ensure parent tasks are processed before their subtasks const sortedTaskIds = taskIds.sort((a, b) => { const aParts = a.split('.').map(Number); const bParts = b.split('.').map(Number); for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) { if (aParts[i] !== bParts[i]) { return aParts[i] - bParts[i]; } } return aParts.length - bParts.length; }); // Validate if deletion is allowed based on deleteChildren flag for (const taskId of sortedTaskIds) { const task = this.tasks.findOne({ goalId, id: taskId }); if (!task) continue; const subtasks = this.tasks.find({ goalId, parentId: taskId }); if (subtasks.length > 0 && !deleteChildren) { throw new Error(`Task ${taskId} has subtasks and cannot be deleted without explicitly setting 'deleteChildren' to true.`); } } // Soft delete the tasks and all their subtasks const softDeleteTaskAndSubtasks = async (taskId: string) => { const task = this.tasks.findOne({ goalId, id: taskId }); if (!task) return; // Add parent to set for status check later if (task.parentId !== null) { parentsToCheck.add(task.parentId); } // First soft delete all subtasks (only if deleteChildren is true, which is checked above) const subtasks = this.tasks.find({ goalId, parentId: taskId }); for (const subtask of subtasks) { await softDeleteTaskAndSubtasks(subtask.id); } // Then soft delete the task itself if (!task.deleted) { task.deleted = true; task.updatedAt = new Date().toISOString(); this.tasks.update(task); const { createdAt, updatedAt, parentId: _, $loki, meta, ...taskData } = task as LokiTask; removedTasks.push(taskData); } }; for (const taskId of sortedTaskIds) { await softDeleteTaskAndSubtasks(taskId); } // Update parent statuses for (const parentId of parentsToCheck) { if (parentId !== null) { const parentTask = this.tasks.findOne({ goalId, id: parentId }); if (parentTask) { // Only consider non-deleted child tasks for parent completion status const childTasks = this.tasks.find({ goalId, parentId, deleted: false }); const allChildrenComplete = childTasks.length > 0 && childTasks.every(task => task.isComplete); if (allChildrenComplete && !parentTask.isComplete) { parentTask.isComplete = true; parentTask.updatedAt = new Date().toISOString(); this.tasks.update(parentTask); const { createdAt, updatedAt, parentId: _, $loki, meta, ...taskData } = parentTask as LokiTask; completedParents.push(taskData); } else if (!allChildrenComplete && parentTask.isComplete) { // If a non-deleted child task is marked incomplete, or a new incomplete non-deleted child is added, // the parent should also become incomplete. parentTask.isComplete = false; parentTask.updatedAt = new Date().toISOString(); this.tasks.update(parentTask); const { createdAt, updatedAt, parentId: _, $loki, meta, ...taskData } = parentTask as LokiTask; completedParents.push(taskData); } } } } plan.updatedAt = new Date().toISOString(); await this.save(); return { removedTasks, completedParents }; }
- src/index.ts:283-296 (handler)MCP tool dispatch handler for 'remove_tasks' that extracts parameters, calls storage.removeTasks, and formats the response.case 'remove_tasks': { const { goalId, taskIds, deleteChildren } = request.params.arguments as { goalId: number; taskIds: string[]; deleteChildren?: boolean }; const results = await storage.removeTasks(goalId, taskIds, deleteChildren); const textContent = JSON.stringify(results, null, 2); return { content: [ { type: 'text', text: textContent, }, ], }; }
- src/index.ts:106-131 (registration)Tool registration in the ListTools response, including name, description, and input schema.{ name: 'remove_tasks', description: 'Soft-delete multiple tasks from a goal. Tasks are marked as deleted but remain in the system. Task IDs use a dot-notation (e.g., "1", "1.1", "1.1.1"). Responses will return simplified task objects without `createdAt`, `updatedAt`, or `parentId`. Soft-deleted tasks are excluded by default from `get_tasks` results unless `includeDeletedTasks` is set to true.', inputSchema: { type: 'object', properties: { goalId: { type: 'number', description: 'ID of the goal to remove tasks from (number)', }, taskIds: { type: 'array', items: { type: 'string', }, description: 'IDs of the tasks to remove (array of strings). Example: ["1", "1.1"].', }, deleteChildren: { type: 'boolean', description: 'Whether to delete child tasks along with the parent (boolean). Defaults to false. If false, attempting to delete a parent task with existing subtasks will throw an error.', default: false, }, }, required: ['goalId', 'taskIds'], }, },
- src/index.ts:109-130 (schema)Input schema definition for the remove_tasks tool, specifying parameters, types, descriptions, and requirements.inputSchema: { type: 'object', properties: { goalId: { type: 'number', description: 'ID of the goal to remove tasks from (number)', }, taskIds: { type: 'array', items: { type: 'string', }, description: 'IDs of the tasks to remove (array of strings). Example: ["1", "1.1"].', }, deleteChildren: { type: 'boolean', description: 'Whether to delete child tasks along with the parent (boolean). Defaults to false. If false, attempting to delete a parent task with existing subtasks will throw an error.', default: false, }, }, required: ['goalId', 'taskIds'], },