Skip to main content
Glama

move_task

Move a ClickUp task to a different list using task ID or name. Specify destination by list ID or name. Note: Task status may reset if destination list has different status options.

Instructions

Move a task to a different list. Valid parameter combinations:

  1. Use taskId + (listId or listName) - preferred

  2. Use taskName + sourceListName + (listId or listName)

WARNING: Task statuses may reset if destination list has different status options.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
taskIdNoID of the task to move (preferred). Use this instead of taskName if you have it.
taskNameNoName of the task to move. When using this, you MUST also provide sourceListName.
sourceListNameNoREQUIRED with taskName: Current list containing the task.
listIdNoID of destination list (preferred). Use this instead of listName if you have it.
listNameNoName of destination list. Only use if you don't have listId.

Implementation Reference

  • The handleMoveTask function is the primary handler executed by the MCP server for 'move_task' tool calls. It handles parameter resolution (IDs from names using workspace hierarchy and task lists), invokes the task service's moveTask method, and returns a formatted MCP content response.
    export async function handleMoveTask(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 // This is less efficient but necessary if source list is unknown 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; } if (!targetListId) { throw new Error("Either listId or listName must be provided for the target list"); } // Move the task const task = await taskService.moveTask(targetTaskId, targetListId); // Format response return { content: [{ type: "text", text: JSON.stringify({ id: task.id, name: task.name, url: task.url, moved: 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) }] }; }
  • The moveTaskTool object defines the schema, name, and description for the 'move_task' tool, returned by the server's ListTools handler. It also includes an inline handler implementation.
    export const moveTaskTool = { name: "move_task", description: "Move a task to a different list. Valid parameter combinations:\n1. Use taskId + (listId or listName) - preferred\n2. Use taskName + sourceListName + (listId or listName)\n\nWARNING: Task statuses may reset if destination list has different status options.", inputSchema: { type: "object", properties: { taskId: { type: "string", description: "ID of the task to move (preferred). Use this instead of taskName if you have it." }, taskName: { type: "string", description: "Name of the task to move. When using this, you MUST also provide sourceListName." }, sourceListName: { type: "string", description: "REQUIRED with taskName: Current list containing the task." }, listId: { type: "string", description: "ID of destination list (preferred). Use this instead of listName if you have it." }, listName: { type: "string", description: "Name of destination list. Only use if you don't have listId." } }, required: [] }, async handler({ taskId, taskName, sourceListName, listId, listName }: { taskId?: string; taskName?: string; sourceListName?: string; listId?: string; listName?: string; }) { let targetTaskId = taskId; let targetListId = listId; // If no taskId but taskName is provided, look up the task ID if (!targetTaskId && taskName) { // First find the source list ID if sourceListName is provided let sourceListId: string | undefined; 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; } // Now find the task 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${sourceListName ? ` in list "${sourceListName}"` : ""}`); } targetTaskId = foundTask.id; } if (!targetTaskId) { throw new Error("Either taskId or taskName must be provided"); } // 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(`List "${listName}" not found`); } targetListId = listInfo.id; } if (!targetListId) { throw new Error("Either listId or listName must be provided"); } // Move the task const movedTask = await taskService.moveTask(targetTaskId, targetListId); // Format response return { content: [{ type: "text", text: JSON.stringify({ id: movedTask.id, name: movedTask.name, url: movedTask.url, status: movedTask.status?.status || "Unknown", due_date: movedTask.due_date ? formatDueDate(Number(movedTask.due_date)) : undefined, list: movedTask.list.name, moved: true }, null, 2) }] }; } };
  • src/server.ts:106-107 (registration)
    Server routes 'move_task' tool calls to the handleMoveTask function in the CallToolRequestSchema handler.
    case "move_task": return handleMoveTask(params);
  • src/server.ts:70-92 (registration)
    moveTaskTool is registered in the list of available tools returned by ListToolsRequestSchema.
    workspaceHierarchyTool, createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, createListTool, createListInFolderTool, getListTool, updateListTool, deleteListTool, createFolderTool, getFolderTool, updateFolderTool, deleteFolderTool ] };
  • Core implementation of task moving in TaskService: fetches original task and destination list, preserves status/priority where possible, creates duplicate in new list, deletes original, returns new task with metadata.
    async moveTask(taskId: string, destinationListId: string): Promise<ClickUpTask> { this.logOperation('moveTask', { taskId, destinationListId }); try { // First, get both the task and list info in parallel to save time const [originalTask, destinationList] = await Promise.all([ this.getTask(taskId), this.listService.getList(destinationListId) ]); const currentStatus = originalTask.status?.status; const availableStatuses = destinationList.statuses?.map(s => s.status) || []; // Determine the appropriate status for the destination list let newStatus = availableStatuses.includes(currentStatus || '') ? currentStatus // Keep the same status if available in destination list : destinationList.statuses?.[0]?.status; // Otherwise use the default (first) status // 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 the task data for the new list const taskData: CreateTaskData = { name: originalTask.name, description: originalTask.description, status: newStatus, priority: priorityValue, due_date: originalTask.due_date ? Number(originalTask.due_date) : undefined, assignees: originalTask.assignees?.map(a => a.id) || [], // Add any other relevant fields from the original task }; // Create new task and delete old one in a single makeRequest call return await this.makeRequest(async () => { // First create the new task const response = await this.client.post<ClickUpTask>( `/list/${destinationListId}/task`, taskData ); // Then delete the original task await this.client.delete(`/task/${taskId}`); // Add a property to indicate the task was moved const newTask = { ...response.data, moved: true, originalId: taskId }; return newTask; }); } catch (error) { throw this.handleError(error, 'Failed to move task'); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/windalfin/clickup-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server