Skip to main content
Glama
windalfin

ClickUp MCP Server

by windalfin

create_bulk_tasks

Create multiple tasks simultaneously in a ClickUp list by providing an array of task details, with optional batch processing and concurrency controls for efficient task management.

Instructions

Create multiple tasks in a list efficiently. You MUST provide:

  1. An array of tasks with required properties

  2. Either listId or listName to specify the target list

Optional: Configure batch size and concurrency for performance.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
listIdNoID of list for new tasks (preferred). Use this instead of listName if you have it.
listNameNoName of list for new tasks. Only use if you don't have listId.
tasksYesArray of tasks to create. Each task must have at least a name.
optionsNoOptional processing settings

Implementation Reference

  • Handler function that processes the create_bulk_tasks tool call. Validates input, resolves list ID if needed, maps tasks to ClickUp format, calls taskService.createBulkTasks, and formats results with success/failure counts.
    export async function handleCreateBulkTasks(parameters: any) {
      // Validate required parameters
      const { tasks, listId, listName } = parameters;
      
      if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
        throw new Error('You must provide a non-empty array of tasks to create');
      }
    
      let targetListId = listId;
          
      // If no listId but listName is provided, look up the list ID
      if (!targetListId && listName) {
        const listInfo = await findListIDByName(workspaceService, listName);
        if (!listInfo) {
          throw new Error(`List "${listName}" not found`);
        }
        targetListId = listInfo.id;
      }
      
      if (!targetListId) {
        throw new Error("Either listId or listName must be provided");
      }
    
      const results = {
        total: tasks.length,
        successful: 0,
        failed: 0,
        failures: [] as any[]
      };
    
      // Map tasks to ClickUp format
      const clickupTasks = tasks.map((task: any) => {
        const taskData: CreateTaskData = {
          name: task.name,
          description: task.description,
          markdown_description: task.markdown_description,
          status: task.status,
          priority: task.priority as TaskPriority,
          due_date: task.dueDate ? parseDueDate(task.dueDate) : undefined
        };
        
        // Add due_date_time flag if due date is set
        if (task.dueDate && taskData.due_date) {
          taskData.due_date_time = true;
        }
        
        return taskData;
      });
    
      // Create tasks in bulk using the task service
      try {
        const bulkResult = await taskService.createBulkTasks(targetListId, { tasks: clickupTasks });
        
        // Update results based on bulk operation outcome
        results.successful = bulkResult.successfulItems.length;
        results.failed = bulkResult.failedItems.length;
        results.failures = bulkResult.failedItems.map(failure => ({
          task: failure.item.name,
          error: failure.error.message
        }));
      } catch (error: any) {
        // If the bulk operation itself fails, mark all tasks as failed
        results.failed = tasks.length;
        results.failures = tasks.map(task => ({
          task: task.name,
          error: error.message
        }));
      }
    
      return {
        content: [{
          type: "text",
          text: JSON.stringify(results, null, 2)
        }]
      };
    }
  • Tool definition object including name, description, and detailed inputSchema for validating parameters like tasks array, listId/listName, and optional batching options.
    export const createBulkTasksTool = {
      name: "create_bulk_tasks",
      description: "Create multiple tasks in a list efficiently. You MUST provide:\n1. An array of tasks with required properties\n2. Either listId or listName to specify the target list\n\nOptional: Configure batch size and concurrency for performance.",
      inputSchema: {
        type: "object",
        properties: {
          listId: {
            type: "string",
            description: "ID of list for new tasks (preferred). Use this instead of listName if you have it."
          },
          listName: {
            type: "string",
            description: "Name of list for new tasks. Only use if you don't have listId."
          },
          tasks: {
            type: "array",
            description: "Array of tasks to create. Each task must have at least a name.",
            items: {
              type: "object",
              properties: {
                name: {
                  type: "string",
                  description: "Task name with emoji prefix"
                },
                description: {
                  type: "string",
                  description: "Plain text description"
                },
                markdown_description: {
                  type: "string",
                  description: "Markdown description (overrides plain text)"
                },
                status: {
                  type: "string",
                  description: "Task status (uses list default if omitted)"
                },
                priority: {
                  type: "number",
                  description: "Priority 1-4 (1=urgent, 4=low)"
                },
                dueDate: {
                  type: "string",
                  description: "Due date (Unix timestamp ms)"
                }
              },
              required: ["name"]
            }
          },
          options: {
            type: "object",
            description: "Optional processing settings",
            properties: {
              batchSize: {
                type: "number",
                description: "Tasks per batch (default: 10)"
              },
              concurrency: {
                type: "number",
                description: "Parallel operations (default: 1)"
              },
              continueOnError: {
                type: "boolean",
                description: "Continue if some tasks fail"
              },
              retryCount: {
                type: "number",
                description: "Retry attempts for failures"
              }
            }
          }
        },
        required: ["tasks"]
      }
    };
  • src/server.ts:99-144 (registration)
    Server request handler switch statement that routes 'create_bulk_tasks' calls to handleCreateBulkTasks. Also registers the tool in the ListTools response at line 78.
    switch (name) {
      case "get_workspace_hierarchy":
        return handleGetWorkspaceHierarchy();
      case "create_task":
        return handleCreateTask(params);
      case "update_task":
        return handleUpdateTask(params);
      case "move_task":
        return handleMoveTask(params);
      case "duplicate_task":
        return handleDuplicateTask(params);
      case "get_task":
        return handleGetTask(params);
      case "get_tasks":
        return handleGetTasks(params);
      case "delete_task":
        return handleDeleteTask(params);
      case "create_bulk_tasks":
        return handleCreateBulkTasks(params);
      case "update_bulk_tasks":
        return handleUpdateBulkTasks(params as { tasks: any[] });
      case "move_bulk_tasks":
        return handleMoveBulkTasks(params as { tasks: any[], targetListId?: string, targetListName?: string });
      case "delete_bulk_tasks":
        return handleDeleteBulkTasks(params as { tasks: any[] });
      case "create_list":
        return handleCreateList(params);
      case "create_list_in_folder":
        return handleCreateListInFolder(params);
      case "get_list":
        return handleGetList(params);
      case "update_list":
        return handleUpdateList(params);
      case "delete_list":
        return handleDeleteList(params);
      case "create_folder":
        return handleCreateFolder(params);
      case "get_folder":
        return handleGetFolder(params);
      case "update_folder":
        return handleUpdateFolder(params);
      case "delete_folder":
        return handleDeleteFolder(params);
      default:
        throw new Error(`Unknown tool: ${name}`);
    }
  • src/server.ts:70-91 (registration)
    ListToolsRequestSchema handler that returns array of tools including createBulkTasksTool for MCP tool discovery.
      workspaceHierarchyTool,
      createTaskTool,
      getTaskTool,
      getTasksTool,
      updateTaskTool,
      moveTaskTool,
      duplicateTaskTool,
      deleteTaskTool,
      createBulkTasksTool,
      updateBulkTasksTool,
      moveBulkTasksTool,
      deleteBulkTasksTool,
      createListTool,
      createListInFolderTool,
      getListTool,
      updateListTool,
      deleteListTool,
      createFolderTool,
      getFolderTool,
      updateFolderTool,
      deleteFolderTool
    ]
  • Core service method in TaskService that implements bulk task creation using BulkProcessor for batching, concurrency, retries, and error handling. Called by the tool handler.
    async createBulkTasks(
      listId: string,
      data: { tasks: Array<CreateTaskData> }, 
      options?: BulkOperationOptions,
      progressCallback?: (progress: ProgressInfo) => void
    ): Promise<BulkOperationResult<ClickUpTask>> {
      this.logOperation('createBulkTasks', { 
        listId, 
        taskCount: data.tasks.length,
        batchSize: options?.batchSize,
        concurrency: options?.concurrency
      });
      
      try {
        // Validate list exists before proceeding
        const list = await this.listService.getList(listId).catch(() => null);
        if (!list) {
          throw new ClickUpServiceError(
            `List not found with ID: ${listId}`,
            ErrorCode.NOT_FOUND
          );
        }
    
        // Set default options for better performance
        const bulkOptions: BulkOperationOptions = {
          batchSize: options?.batchSize ?? 5, // Smaller batch size for better rate limit handling
          concurrency: options?.concurrency ?? 2, // Lower concurrency to avoid rate limits
          continueOnError: options?.continueOnError ?? true, // Continue on individual task failures
          retryCount: options?.retryCount ?? 3, // Retry failed operations
          ...options
        };
    
        // Add list validation to progress tracking
        const wrappedCallback = progressCallback ? 
          (progress: ProgressInfo) => {
            progress.context = { listId, listName: list.name };
            progressCallback(progress);
          } : undefined;
    
        return await this.bulkProcessor.processBulk(
          data.tasks,
          async (taskData) => {
            try {
              // Ensure task data is properly formatted
              const sanitizedData = {
                ...taskData,
                // Remove any accidentally included list IDs in task data
                list: undefined,
                // Ensure name has emoji if missing
                name: taskData.name.match(/^\p{Emoji}/u) ? 
                  taskData.name : 
                  '📋 ' + taskData.name
              };
    
              return await this.createTask(listId, sanitizedData);
            } catch (error) {
              // Enhance error context for better debugging
              if (error instanceof ClickUpServiceError) {
                error.context = {
                  ...error.context,
                  taskName: taskData.name,
                  listId,
                  listName: list.name
                };
              }
              throw error;
            }
          },
          bulkOptions
        );
      } catch (error: any) {
        const errorMessage = error instanceof ClickUpServiceError ?
          error.message :
          `Failed to create tasks in bulk: ${error.message}`;
    
        throw new ClickUpServiceError(
          errorMessage,
          error instanceof ClickUpServiceError ? error.code : ErrorCode.UNKNOWN,
          {
            listId,
            taskCount: data.tasks.length,
            error: error instanceof Error ? error.message : String(error)
          }
        );
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It discloses that this is a creation operation (implying mutation) and mentions performance configuration options. However, it doesn't cover important behavioral aspects like error handling (beyond the optional 'continueOnError' in schema), rate limits, authentication requirements, or what happens when tasks fail partially. The description adds some context but leaves significant gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is perfectly structured with bullet points, front-loading the core purpose and immediately listing requirements. Every sentence earns its place - no redundant information, no fluff. The optional performance configuration is mentioned efficiently at the end.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations and no output schema, the description is adequate but incomplete. It covers the basic purpose and requirements well, but lacks information about return values, error responses, side effects, or system behavior. Given the complexity (4 parameters with nested objects) and absence of structured behavioral metadata, the description should do more to compensate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal value beyond the schema - it emphasizes the required array of tasks and the listId/listName choice, but doesn't provide additional semantic context about parameter interactions or usage patterns that aren't already in the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Create multiple tasks' and specifies the resource 'in a list efficiently', distinguishing it from sibling tools like 'create_task' (singular) and 'create_list' (different resource). It explicitly mentions bulk creation, which sets it apart from single-task operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use this tool (creating multiple tasks efficiently) and mentions required inputs (array of tasks, listId or listName). However, it doesn't explicitly state when NOT to use it or name specific alternatives like 'create_task' for single tasks, which would be helpful for sibling differentiation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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