Skip to main content
Glama
windalfin

ClickUp MCP Server

by windalfin

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