update_task
Modify task details, dependencies, priorities, and statuses to maintain accurate and up-to-date project workflows. Supports hierarchy adjustments, time tracking, and advanced project management.
Instructions
Adapt and refine tasks with comprehensive updates including dependencies, priorities, complexity, status, tags, and time tracking. Keep your workflow current and accurate with advanced project management capabilities including unlimited hierarchy movement.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| actualHours | No | Actual time spent on the task in hours | |
| completed | No | Mark task as completed (true) or incomplete (false) (optional) | |
| complexity | No | Updated complexity/effort estimate (1-10, where 10 is most complex) | |
| dependsOn | No | Updated array of task IDs that must be completed before this task | |
| details | No | New detailed description for the task (optional) | |
| estimatedHours | No | Updated estimated time to complete in hours | |
| id | Yes | The unique identifier of the task to update | |
| name | No | New name/title for the task (optional) | |
| parentId | No | Updated parent task ID for moving between hierarchy levels (optional - use null/empty to move to top level) | |
| priority | No | Updated task priority level (1-10, where 10 is highest priority) | |
| status | No | Updated task status | |
| tags | No | Updated tags for categorization and filtering | |
| workingDirectory | Yes | The full absolute path to the working directory where data is stored. MUST be an absolute path, never relative. Windows: "C:\Users\username\project" or "D:\projects\my-app". Unix/Linux/macOS: "/home/username/project" or "/Users/username/project". Do NOT use: ".", "..", "~", "./folder", "../folder" or any relative paths. Ensure the path exists and is accessible before calling this tool. NOTE: When server is started with --claude flag, this parameter is ignored and a global user directory is used instead. |
Implementation Reference
- Main handler for 'update_task' tool. Validates inputs, checks for hierarchy cycles and constraints, calls storage.updateTask, formats success/error responses with hierarchy info.export function createUpdateTaskTool(storage: Storage) { return { name: 'update_task', description: 'Update task properties including name, details, parent relationship (parentId), completion status, dependencies, priority, complexity, status, tags, and time estimates. Use parentId to move tasks within the hierarchy.', inputSchema: { id: z.string(), name: z.string().optional(), details: z.string().optional(), parentId: z.string().optional(), completed: z.boolean().optional(), dependsOn: z.array(z.string()).optional(), priority: z.number().min(1).max(10).optional(), complexity: z.number().min(1).max(10).optional(), status: z.enum(['pending', 'in-progress', 'blocked', 'done']).optional(), tags: z.array(z.string()).optional(), estimatedHours: z.number().min(0).optional(), actualHours: z.number().min(0).optional() }, handler: async ({ id, name, details, parentId, completed, dependsOn, priority, complexity, status, tags, estimatedHours, actualHours }: { id: string; name?: string; details?: string; parentId?: string; completed?: boolean; dependsOn?: string[]; priority?: number; complexity?: number; status?: 'pending' | 'in-progress' | 'blocked' | 'done'; tags?: string[]; estimatedHours?: number; actualHours?: number; }) => { try { // Validate inputs if (!id || id.trim().length === 0) { return { content: [{ type: 'text' as const, text: 'Error: Task ID is required.' }], isError: true }; } if (name !== undefined && (!name || name.trim().length === 0)) { return { content: [{ type: 'text' as const, text: 'Error: Task name must not be empty.' }], isError: true }; } if (name !== undefined && name.trim().length > 100) { return { content: [{ type: 'text' as const, text: 'Error: Task name must be 100 characters or less.' }], isError: true }; } if (details !== undefined && (!details || details.trim().length === 0)) { return { content: [{ type: 'text' as const, text: 'Error: Task details must not be empty.' }], isError: true }; } if (details !== undefined && details.trim().length > 2000) { return { content: [{ type: 'text' as const, text: 'Error: Task details must be 2000 characters or less.' }], isError: true }; } if (name === undefined && details === undefined && parentId === undefined && completed === undefined && dependsOn === undefined && priority === undefined && complexity === undefined && status === undefined && tags === undefined && estimatedHours === undefined && actualHours === undefined) { return { content: [{ type: 'text' as const, text: 'Error: At least one field must be provided for update.' }], isError: true }; } const existingTask = await storage.getTask(id.trim()); if (!existingTask) { return { content: [{ type: 'text' as const, text: `Error: Task with ID "${id}" not found. Use list_tasks to see all available tasks.` }], isError: true }; } // Validate parentId if provided let newParentTask = null; if (parentId !== undefined) { if (parentId) { if (parentId === id) { return { content: [{ type: 'text' as const, text: `Error: Task cannot be its own parent.` }], isError: true }; } newParentTask = await storage.getTask(parentId.trim()); if (!newParentTask) { return { content: [{ type: 'text' as const, text: `Error: Parent task with ID "${parentId}" not found.` }], isError: true }; } // Ensure parent is in the same project if (newParentTask.projectId !== existingTask.projectId) { return { content: [{ type: 'text' as const, text: `Error: Parent task must be in the same project.` }], isError: true }; } // Check for circular dependencies (would the new parent be a descendant?) const children = await storage.getTaskChildren(id); const allDescendants = await getAllDescendants(storage, id); if (allDescendants.includes(parentId)) { return { content: [{ type: 'text' as const, text: `Error: Cannot move task under its own descendant. This would create a circular hierarchy.` }], isError: true }; } } } // Check for name uniqueness within the same parent scope if name is being updated if (name && name.toLowerCase() !== existingTask.name.toLowerCase()) { const effectiveParentId = parentId !== undefined ? parentId : existingTask.parentId; const siblingTasks = await storage.getTasks(existingTask.projectId, effectiveParentId); const nameExists = siblingTasks.some(t => t.id !== id && t.name.toLowerCase() === name.toLowerCase()); if (nameExists) { const scopeDescription = newParentTask ? `under parent task "${newParentTask.name}"` : effectiveParentId ? 'in the current parent scope' : 'at the top level of this project'; return { content: [{ type: 'text' as const, text: `Error: A task with the name "${name}" already exists ${scopeDescription}. Please choose a different name.` }], isError: true }; } } // Validate dependencies exist if provided if (dependsOn && dependsOn.length > 0) { for (const depId of dependsOn) { if (depId === id) { return { content: [{ type: 'text' as const, text: `Error: Task cannot depend on itself.` }], isError: true }; } const depTask = await storage.getTask(depId); if (!depTask) { return { content: [{ type: 'text' as const, text: `Error: Dependency task with ID "${depId}" not found.` }], isError: true }; } } } const updates: any = { updatedAt: new Date().toISOString() }; if (name !== undefined) { updates.name = name.trim(); } if (details !== undefined) { updates.details = details.trim(); } if (parentId !== undefined) { updates.parentId = parentId?.trim() || undefined; } if (completed !== undefined) { updates.completed = completed; } if (dependsOn !== undefined) { updates.dependsOn = dependsOn; } if (priority !== undefined) { updates.priority = priority; } if (complexity !== undefined) { updates.complexity = complexity; } if (status !== undefined) { updates.status = status; } if (tags !== undefined) { updates.tags = tags; } if (estimatedHours !== undefined) { updates.estimatedHours = estimatedHours; } if (actualHours !== undefined) { updates.actualHours = actualHours; } const updatedTask = await storage.updateTask(id, updates); if (!updatedTask) { return { content: [{ type: 'text' as const, text: `Error: Failed to update task with ID "${id}".` }], isError: true }; } // Get project and hierarchy information for display const project = await storage.getProject(updatedTask.projectId); const projectName = project ? project.name : 'Unknown Project'; const currentParent = updatedTask.parentId ? await storage.getTask(updatedTask.parentId) : null; const taskLevel = updatedTask.level || 0; const changedFields = []; if (name !== undefined) changedFields.push('name'); if (details !== undefined) changedFields.push('details'); if (parentId !== undefined) changedFields.push('parent relationship'); if (completed !== undefined) changedFields.push('completion status'); if (dependsOn !== undefined) changedFields.push('dependencies'); if (priority !== undefined) changedFields.push('priority'); if (complexity !== undefined) changedFields.push('complexity'); if (status !== undefined) changedFields.push('status'); if (tags !== undefined) changedFields.push('tags'); if (estimatedHours !== undefined) changedFields.push('estimated hours'); if (actualHours !== undefined) changedFields.push('actual hours'); const taskStatus = updatedTask.status || (updatedTask.completed ? 'done' : 'pending'); const levelIndicator = ' '.repeat(taskLevel) + '→'; // Build hierarchy path let hierarchyPath = projectName; if (currentParent) { const ancestors = await storage.getTaskAncestors(updatedTask.id); hierarchyPath = `${projectName} → ${ancestors.map(a => a.name).join(' → ')} → ${updatedTask.name}`; } else { hierarchyPath = `${projectName} → ${updatedTask.name}`; } return { content: [{ type: 'text' as const, text: `✅ Task updated successfully! **${levelIndicator} ${updatedTask.name}** (ID: ${updatedTask.id}) ${currentParent ? `Parent: ${currentParent.name} (${currentParent.id})` : 'Top-level task'} Project: ${projectName} Level: ${taskLevel} ${taskLevel === 0 ? '(Top-level)' : `(${taskLevel} level${taskLevel > 1 ? 's' : ''} deep)`} Path: ${hierarchyPath} 📋 **Task Properties:** • Priority: ${updatedTask.priority || 'Not set'}/10 • Complexity: ${updatedTask.complexity || 'Not set'}/10 • Status: ${taskStatus} • Completed: ${updatedTask.completed ? 'Yes' : 'No'} • Tags: ${updatedTask.tags?.join(', ') || 'None'} • Dependencies: ${updatedTask.dependsOn?.length ? updatedTask.dependsOn.join(', ') : 'None'} • Estimated Hours: ${updatedTask.estimatedHours || 'Not set'} • Actual Hours: ${updatedTask.actualHours || 'Not set'} • Details: ${updatedTask.details} • Last Updated: ${new Date(updatedTask.updatedAt).toLocaleString()} ✏️ **Updated fields:** ${changedFields.join(', ')} 🎯 **Next Steps:** ${parentId !== undefined ? '• Use `list_tasks` to see the updated hierarchy structure' : ''} • Use \`get_next_task_recommendation\` to see what to work on next • Run \`analyze_task_complexity\` if complexity has changed ${taskLevel > 0 ? '• Consider breaking down further with create_task using this task as parentId' : ''}` }] }; } catch (error) { return { content: [{ type: 'text' as const, text: `Error updating task: ${error instanceof Error ? error.message : 'Unknown error'}` }], isError: true }; } } }; }
- TypeScript interface defining the UpdateTaskInput shape, matching the tool's inputSchema for validation.export interface UpdateTaskInput { /** Task name (optional) */ name?: string; /** Enhanced task description (optional) */ details?: string; /** Reference to parent task (optional) */ parentId?: string; /** Task completion status (optional) */ completed?: boolean; /** Task dependencies - IDs of tasks that must be completed before this task */ dependsOn?: string[]; /** Task priority level (1-10, where 10 is highest priority) */ priority?: number; /** Estimated complexity/effort (1-10, where 10 is most complex) */ complexity?: number; /** Task status */ status?: 'pending' | 'in-progress' | 'blocked' | 'done'; /** Tags for categorization and filtering */ tags?: string[]; /** Estimated time to complete in hours */ estimatedHours?: number; /** Actual time spent in hours */ actualHours?: number; }
- src/features/task-management/tools/tasks/index.ts:235-245 (registration)Registers the 'update_task' tool by including createUpdateTaskTool(storage) in the createTaskTools export.export function createTaskTools(storage: Storage) { return { create_task: createCreateTaskTool(storage), delete_task: createDeleteTaskTool(storage), get_task: createGetTaskTool(storage), list_tasks: createListTasksTool(storage), update_task: createUpdateTaskTool(storage), migrate_subtasks: createMigrateSubtasksTool(storage), move_task: createMoveTaskTool(storage) }; }
- Core storage method updateTask called by the tool handler to persist task updates to JSON file, including hierarchy validation.async updateTask(id: string, updates: Partial<Task>): Promise<Task | null> { const index = this.data.tasks.findIndex(t => t.id === id); if (index === -1) return null; const task = this.data.tasks[index]; // If updating parentId, validate the new parent if (updates.parentId !== undefined) { if (updates.parentId) { const parent = await this.getTask(updates.parentId); if (!parent) { throw new Error(`Parent task with id ${updates.parentId} not found`); } // Prevent circular references if (await this.wouldCreateCircularReference(id, updates.parentId)) { throw new Error(`Moving task would create a circular reference`); } } } this.data.tasks[index] = { ...task, ...updates }; this.data.tasks[index].level = this.calculateTaskLevel(this.data.tasks[index]); await this.save(); return this.data.tasks[index]; }
- Recursive helper to get all descendant task IDs, used to prevent circular hierarchy when updating parentId.async function getAllDescendants(storage: Storage, taskId: string): Promise<string[]> { const children = await storage.getTaskChildren(taskId); const descendants: string[] = []; for (const child of children) { descendants.push(child.id); const childDescendants = await getAllDescendants(storage, child.id); descendants.push(...childDescendants); } return descendants; }