duplicate_task
Copy a ClickUp task to the same or different list while preserving all original properties. Use task ID or name with optional destination list specification.
Instructions
Create a copy of a task in the same or different list. Valid parameter combinations:
Use taskId + optional (listId or listName) - preferred
Use taskName + sourceListName + optional (listId or listName)
The duplicate preserves the original task's properties.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| taskId | No | ID of task to duplicate (preferred). Use this instead of taskName if you have it. | |
| taskName | No | Name of task to duplicate. When using this, you MUST provide sourceListName. | |
| sourceListName | No | REQUIRED with taskName: List containing the original task. | |
| listId | No | ID of list for the duplicate (optional). Defaults to same list as original. | |
| listName | No | Name of list for the duplicate. Only use if you don't have listId. |
Implementation Reference
- src/server.ts:67-93 (registration)Tool registration in ListToolsRequestSchema handler. duplicateTaskTool is included in the tools list at line 76.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ workspaceHierarchyTool, createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, createListTool, createListInFolderTool, getListTool, updateListTool, deleteListTool, createFolderTool, getFolderTool, updateFolderTool, deleteFolderTool ] }; });
- src/server.ts:108-109 (registration)Dispatch registration in CallToolRequestSchema switch statement.case "duplicate_task": return handleDuplicateTask(params);
- src/tools/task.ts:383-407 (schema)Input schema definition for the duplicate_task tool.inputSchema: { type: "object", properties: { taskId: { type: "string", description: "ID of task to duplicate (preferred). Use this instead of taskName if you have it." }, taskName: { type: "string", description: "Name of task to duplicate. When using this, you MUST provide sourceListName." }, sourceListName: { type: "string", description: "REQUIRED with taskName: List containing the original task." }, listId: { type: "string", description: "ID of list for the duplicate (optional). Defaults to same list as original." }, listName: { type: "string", description: "Name of list for the duplicate. Only use if you don't have listId." } }, required: []
- src/tools/task.ts:1297-1367 (handler)Primary handler function for duplicate_task, called from server.ts CallTool dispatch. Resolves task and list IDs, invokes taskService.duplicateTask, formats MCP response.export async function handleDuplicateTask(parameters: any) { const { taskId, taskName, sourceListName, listId, listName } = parameters; let targetTaskId = taskId; let sourceListId: string | undefined; // If sourceListName is provided, find the source list ID if (sourceListName) { const hierarchy = await workspaceService.getWorkspaceHierarchy(); const listInfo = workspaceService.findIDByNameInHierarchy(hierarchy, sourceListName, 'list'); if (!listInfo) { throw new Error(`Source list "${sourceListName}" not found`); } sourceListId = listInfo.id; } // If no taskId but taskName is provided, look up the task ID if (!targetTaskId && taskName) { // Find the task in the source list if specified, otherwise search all tasks if (sourceListId) { const tasks = await taskService.getTasks(sourceListId); const foundTask = tasks.find(t => t.name.toLowerCase() === taskName.toLowerCase()); if (!foundTask) { throw new Error(`Task "${taskName}" not found in list "${sourceListName}"`); } targetTaskId = foundTask.id; } else { // Without a source list, we need to search more broadly throw new Error("When using taskName, sourceListName must be provided to find the task"); } } if (!targetTaskId) { throw new Error("Either taskId or taskName (with sourceListName) must be provided"); } let targetListId = listId; // If no listId but listName is provided, look up the list ID if (!targetListId && listName) { const hierarchy = await workspaceService.getWorkspaceHierarchy(); const listInfo = workspaceService.findIDByNameInHierarchy(hierarchy, listName, 'list'); if (!listInfo) { throw new Error(`Target list "${listName}" not found`); } targetListId = listInfo.id; } // Duplicate the task const task = await taskService.duplicateTask(targetTaskId, targetListId); // Format response return { content: [{ type: "text", text: JSON.stringify({ id: task.id, name: task.name, url: task.url, duplicated: true, due_date: task.due_date ? formatDueDate(Number(task.due_date)) : undefined, list: task.list.name, space: task.space.name, folder: task.folder?.name }, null, 2) }] }; }
- src/services/clickup/task.ts:296-332 (helper)TaskService.duplicateTask helper method: fetches original task, creates copy with '(Copy)' suffix in target list (or same), handles priority mapping, calls ClickUp API.async duplicateTask(taskId: string, listId?: string): Promise<ClickUpTask> { this.logOperation('duplicateTask', { taskId, listId }); try { // First, get the original task const originalTask = await this.getTask(taskId); // Priority mapping: convert string priority to numeric value if needed let priorityValue = null; if (originalTask.priority) { // If priority.id exists and is numeric, use that if (originalTask.priority.id) { priorityValue = parseInt(originalTask.priority.id); // Ensure it's in the valid range 1-4 if (isNaN(priorityValue) || priorityValue < 1 || priorityValue > 4) { priorityValue = null; } } } // Prepare data for the new task const newTaskData: CreateTaskData = { name: `${originalTask.name} (Copy)`, description: originalTask.description, status: originalTask.status?.status, priority: priorityValue, due_date: originalTask.due_date ? new Date(originalTask.due_date).getTime() : undefined, assignees: originalTask.assignees?.map(a => a.id) || [] }; // Create the new task in the specified list or original list const targetListId = listId || originalTask.list.id; return await this.createTask(targetListId, newTaskData); } catch (error) { throw this.handleError(error, 'Failed to duplicate task'); } }