Skip to main content
Glama
greirson

Todoist MCP Server

todoist_tasks_bulk_delete

Delete multiple Todoist tasks simultaneously using filters like project, priority, due date, or content to remove outdated or unwanted items in bulk.

Instructions

Delete multiple tasks at once based on search criteria. Use with caution - this will permanently delete matching tasks.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_idNoDelete tasks from this project ID (optional)
priorityNoDelete tasks with this priority level 1 (highest) to 4 (lowest) (optional)
due_beforeNoDelete tasks due before this date (YYYY-MM-DD) (optional)
due_afterNoDelete tasks due after this date (YYYY-MM-DD) (optional)
content_containsNoDelete tasks containing this text in content (optional)

Implementation Reference

  • The primary handler function that fetches all tasks, filters them based on the provided criteria, deletes the matching tasks using the Todoist API, handles individual task deletion errors, supports dry-run mode, and returns a formatted summary of successes and failures.
    export async function handleBulkDeleteTasks(
      todoistClient: TodoistApi,
      args: BulkTaskFilterArgs
    ): Promise<string> {
      try {
        // Clear cache since we're deleting
        taskCache.clear();
    
        validateBulkSearchCriteria(args.search_criteria);
    
        const result = await todoistClient.getTasks();
        const allTasks = extractArrayFromResponse<TodoistTask>(result);
        const matchingTasks = filterTasksByCriteria(allTasks, args.search_criteria);
    
        if (matchingTasks.length === 0) {
          return "No tasks found matching the search criteria.";
        }
    
        const deletedTasks: string[] = [];
        const errors: string[] = [];
    
        for (const task of matchingTasks) {
          try {
            await todoistClient.deleteTask(task.id);
            deletedTasks.push(task.content);
          } catch (error) {
            errors.push(
              `Failed to delete task "${task.content}": ${(error as Error).message}`
            );
          }
        }
    
        const successCount = deletedTasks.length;
        const errorCount = errors.length;
    
        // Check if we're in dry-run mode
        const isDryRun = process.env.DRYRUN === "true";
        const prefix = isDryRun ? "[DRY-RUN] " : "";
    
        let response = `${prefix}Bulk delete completed: ${successCount} deleted, ${errorCount} failed.\n\n`;
    
        if (successCount > 0) {
          response += "Deleted tasks:\n";
          response += deletedTasks.map((content) => `- ${content}`).join("\n");
          response += "\n\n";
        }
    
        if (errorCount > 0) {
          response += "Errors:\n";
          response += errors.join("\n");
        }
    
        return response.trim();
      } catch (error) {
        ErrorHandler.handleAPIError("bulk delete tasks", error);
      }
    }
  • Defines the Tool object for 'todoist_tasks_bulk_delete' including name, description, and input schema specifying optional filter parameters like project_id, priority, due dates, and content_contains.
    export const BULK_DELETE_TASKS_TOOL: Tool = {
      name: "todoist_tasks_bulk_delete",
      description:
        "Delete multiple tasks at once based on search criteria. Use with caution - this will permanently delete matching tasks.",
      inputSchema: {
        type: "object",
        properties: {
          project_id: {
            type: "string",
            description: "Delete tasks from this project ID (optional)",
          },
          priority: {
            type: "number",
            description:
              "Delete tasks with this priority level 1 (highest) to 4 (lowest) (optional)",
            enum: [1, 2, 3, 4],
          },
          due_before: {
            type: "string",
            description:
              "Delete tasks due before this date (YYYY-MM-DD) (optional)",
          },
          due_after: {
            type: "string",
            description: "Delete tasks due after this date (YYYY-MM-DD) (optional)",
          },
          content_contains: {
            type: "string",
            description: "Delete tasks containing this text in content (optional)",
          },
        },
        required: [],
      },
    };
  • src/index.ts:226-231 (registration)
    Registers the tool in the main server request handler switch statement: validates arguments using isBulkTaskFilterArgs type guard and invokes the handleBulkDeleteTasks function.
    case "todoist_tasks_bulk_delete":
      if (!isBulkTaskFilterArgs(args)) {
        throw new Error("Invalid arguments for todoist_tasks_bulk_delete");
      }
      result = await handleBulkDeleteTasks(apiClient, args);
      break;
  • Helper function used by bulk handlers to filter tasks based on project, priority, content_contains, and due date criteria. Handles edge cases like empty content search and date comparisons.
    function filterTasksByCriteria(
      tasks: TodoistTask[],
      criteria: BulkTaskFilterArgs["search_criteria"]
    ): TodoistTask[] {
      return tasks.filter((task) => {
        if (criteria.project_id && task.projectId !== criteria.project_id)
          return false;
        const apiPriorityFilter = toApiPriority(criteria.priority);
        if (apiPriorityFilter !== undefined && task.priority !== apiPriorityFilter)
          return false;
        // Fix for issue #34: Handle empty string in content_contains
        if (criteria.content_contains !== undefined) {
          // Treat empty or whitespace-only strings as "no match"
          const searchTerm = criteria.content_contains.trim();
          if (searchTerm === "") {
            // Empty search should match nothing, not everything
            return false;
          }
          if (!task.content.toLowerCase().includes(searchTerm.toLowerCase())) {
            return false;
          }
        }
    
        if (criteria.due_before || criteria.due_after) {
          const taskDate = getDueDateOnly(task.due);
          if (!taskDate) {
            return false;
          }
    
          const isBeforeThreshold =
            !criteria.due_before || taskDate < criteria.due_before;
          const isAfterThreshold =
            !criteria.due_after || taskDate > criteria.due_after;
    
          if (!isBeforeThreshold || !isAfterThreshold) {
            return false;
          }
        }
    
        return true;
      });
    }
  • Adds BULK_DELETE_TASKS_TOOL to the TASK_TOOLS export array, which is imported into the main ALL_TOOLS list provided by the MCP server for tool discovery.
    export const TASK_TOOLS = [
      CREATE_TASK_TOOL,
      GET_TASKS_TOOL,
      UPDATE_TASK_TOOL,
      DELETE_TASK_TOOL,
      COMPLETE_TASK_TOOL,
      BULK_CREATE_TASKS_TOOL,
      BULK_UPDATE_TASKS_TOOL,
      BULK_DELETE_TASKS_TOOL,
      BULK_COMPLETE_TASKS_TOOL,
    ];
Behavior4/5

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

With no annotations provided, the description carries the full burden. It discloses critical behavioral traits: the operation is destructive ('permanently delete'), irreversible, and applies to multiple tasks. It adds value beyond the schema by warning about permanence and caution, though it could mention authentication needs or rate limits.

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 front-loaded with the core action and resource, followed by a crucial warning. Both sentences earn their place: the first defines the tool, and the second adds essential behavioral context. No wasted words, making it highly efficient.

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

Completeness4/5

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

Given the complexity of a destructive bulk operation with no annotations or output schema, the description is reasonably complete. It covers purpose, risk, and scope, but could improve by mentioning prerequisites (e.g., authentication) or response format. It adequately supports agent decision-making for this tool type.

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 fully documents all 5 parameters. The description adds no specific parameter semantics beyond implying search criteria, which is already covered in the schema. Baseline 3 is appropriate as the schema does the heavy lifting.

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 specific action ('Delete multiple tasks at once') and resource ('tasks'), distinguishing it from siblings like todoist_task_delete (single task) and todoist_tasks_bulk_complete (complete vs delete). It explicitly mentions the bulk nature and search criteria, making the purpose unambiguous.

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 with 'Use with caution - this will permanently delete matching tasks,' indicating high-risk usage. However, it does not explicitly state when to use this tool versus alternatives like todoist_task_delete or todoist_tasks_bulk_complete, missing explicit 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/greirson/mcp-todoist'

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