Skip to main content
Glama

update_tasks

Modify existing Todoist tasks by updating their content, due dates, priorities, labels, or other attributes using either task ID or name for identification.

Instructions

Update tasks in Todoist Either 'task_id' or the 'task_name' to identify the target.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
itemsYes

Implementation Reference

  • Core batch API handler logic for 'update_tasks': processes batch of items supporting task_id or task_name lookup, validates path params, performs POST /tasks/{id} via todoistApi with update fields, returns per-item results and summary.
    const handler = async (args: z.infer<typeof batchSchema>): Promise<any> => {
        const { items } = args;
    
        // For modes other than create, check if name lookup is needed
        let allItems: any[] = [];
    
        const needsNameLookup =
            options.mode !== 'create' &&
            options.nameField &&
            options.findByName &&
            items.some(item => item[options.nameField!] && !item[options.idField!]);
    
        if (needsNameLookup) {
            // Determine the base path for fetching all items
            // Example: /tasks from /tasks/{id}
            const lookupPath =
                options.basePath || (options.path ? options.path.split('/{')[0] : '');
            allItems = await todoistApi.get(lookupPath, {});
        }
    
        const results = await Promise.all(
            items.map(async item => {
                if (options.validateItem) {
                    const validation = options.validateItem(item);
    
                    if (!validation.valid) {
                        return {
                            success: false,
                            error: validation.error || 'Validation failed',
                            item,
                        };
                    }
                }
    
                try {
                    let finalPath = '';
                    const apiParams = { ...item };
    
                    // For modes where need id
                    if (options.mode !== 'create' && options.idField) {
                        let itemId = item[options.idField];
                        let matchedName = null;
                        let matchedContent = null;
    
                        // If no ID but name is provided, search by name
                        if (!itemId && item[options.nameField!] && options.findByName) {
                            const searchName = item[options.nameField!];
                            const matchedItem = options.findByName(searchName, allItems);
    
                            if (!matchedItem) {
                                return {
                                    success: false,
                                    error: `Item not found with name: ${searchName}`,
                                    item,
                                };
                            }
    
                            itemId = matchedItem.id;
                            matchedName = searchName;
                            matchedContent = matchedItem.content;
                        }
    
                        if (!itemId) {
                            return {
                                success: false,
                                error: `Either ${options.idField} or ${options.nameField} must be provided`,
                                item,
                            };
                        }
    
                        // Apply security validation to itemId before using in path
                        const safeItemId = validatePathParameter(itemId, options.idField || 'id');
    
                        if (options.basePath && options.pathSuffix) {
                            finalPath = `${options.basePath}${options.pathSuffix.replace('{id}', safeItemId)}`;
                        } else if (options.path) {
                            finalPath = options.path.replace('{id}', safeItemId);
                        }
    
                        delete apiParams[options.idField];
                        if (options.nameField) {
                            delete apiParams[options.nameField];
                        }
    
                        let result;
                        switch (options.method) {
                            case 'GET':
                                result = await todoistApi.get(finalPath, apiParams);
                                break;
                            case 'POST':
                                result = await todoistApi.post(finalPath, apiParams);
                                break;
                            case 'DELETE':
                                result = await todoistApi.delete(finalPath);
                                break;
                        }
    
                        const response: any = {
                            success: true,
                            id: itemId,
                            result,
                        };
    
                        if (matchedName) {
                            response.found_by_name = matchedName;
                            response.matched_content = matchedContent;
                        }
    
                        return response;
                    }
                    // Create mode
                    else {
                        finalPath = options.path || options.basePath || '';
    
                        let result;
                        switch (options.method) {
                            case 'GET':
                                result = await todoistApi.get(finalPath, apiParams);
                                break;
                            case 'POST':
                                result = await todoistApi.post(finalPath, apiParams);
                                break;
                            case 'DELETE':
                                result = await todoistApi.delete(finalPath);
                                break;
                        }
    
                        return {
                            success: true,
                            created_item: result,
                        };
                    }
                } catch (error) {
                    return {
                        success: false,
                        error: error instanceof Error ? error.message : String(error),
                        item,
                    };
                }
            })
        );
    
        const successCount = results.filter(r => r.success).length;
        return {
            success: successCount === items.length,
            summary: {
                total: items.length,
                succeeded: successCount,
                failed: items.length - successCount,
            },
            results,
        };
    };
  • Registers the 'update_tasks' tool via createBatchApiHandler: sets name, description, itemSchema (task_id/name + create_fields), method POST, path '/tasks/{id}', mode 'update', findByName helper.
    createBatchApiHandler({
        name: 'update_tasks',
        description: 'Update tasks in Todoist',
        itemSchema: {
            task_id: z.string().optional(),
            task_name: z.string().optional(),
            ...create_fields,
        },
        method: 'POST',
        path: '/tasks/{id}',
        mode: 'update',
        idField: 'task_id',
        nameField: 'task_name',
        findByName: (name, items) =>
            items.find(item => item.content.toLowerCase().includes(name.toLowerCase())),
    });
  • Common Zod schema (create_fields) for task updates: content, description, labels, priority, due_string/date/datetime/lang, assignee_id, duration/unit, deadline_*.
    /// Common fields for create and update tasks
    const create_fields = {
        content: z
            .string()
            .describe('Task title (brief). May contain markdown-formatted text and hyperlinks'),
        description: z
            .string()
            .optional()
            .describe('Description (detailed). May contain markdown-formatted text and hyperlinks'),
        labels: z.array(z.string()).optional(),
        priority: z.number().int().min(1).max(4).optional().describe('From 1 (urgent) to 4 (normal)'),
        due_string: z
            .string()
            .optional()
            .describe(
                'Human defined task due date (ex.: "next Monday", "Tomorrow"). Value is set using local (not UTC) time, if not in english provided, due_lang should be set to the language of the string'
            ),
        due_date: z
            .string()
            .optional()
            .describe(
                'Due date in YYYY-MM-DD format relative to user timezone (when you plan to work on task)'
            ),
        due_datetime: z.string().optional().describe('Specific date and time in RFC3339 format in UTC'),
        due_lang: z
            .string()
            .optional()
            .describe('2-letter code specifying language in case due_string is not written in english'),
        assignee_id: z
            .string()
            .optional()
            .describe('The responsible user ID (only applies to shared tasks)'),
        duration: z
            .number()
            .int()
            .positive()
            .optional()
            .describe(
                'A positive (greater than zero) integer for the amount of duration_unit the task will take'
            ),
        duration_unit: z
            .enum(['minute', 'day'])
            .optional()
            .describe(
                'The unit of time that the duration field represents. Must be either minute or day'
            ),
        deadline_date: z
            .string()
            .optional()
            .describe(
                'Deadline date in YYYY-MM-DD format relative to user timezone (fixed date when task must be completed, for tasks with external consequences)'
            ),
        deadline_lang: z.string().optional().describe('2-letter code specifying language of deadline'),
    };
  • Specific itemSchema for update_tasks: task_id (opt), task_name (opt), spreads create_fields.
    description: 'Update tasks in Todoist',
    itemSchema: {
        task_id: z.string().optional(),
        task_name: z.string().optional(),
        ...create_fields,
    },
  • findByName helper: finds task by partial content match (case-insensitive).
    findByName: (name, items) =>
        items.find(item => item.content.toLowerCase().includes(name.toLowerCase())),
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions the tool 'Update tasks' which implies a mutation operation, but it fails to describe critical behaviors such as authentication requirements, rate limits, error handling, or what happens if multiple tasks are updated. The description adds minimal context beyond the name, leaving significant gaps in understanding how the tool behaves in practice.

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

Conciseness4/5

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

The description is brief with two sentences, making it efficient and front-loaded. However, it could be more structured by explicitly separating identification from update capabilities. While concise, it risks under-specification given the tool's complexity, but it avoids unnecessary verbosity.

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

Completeness2/5

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

Given the complexity of the tool with 1 parameter (an array of objects with multiple sub-properties), no annotations, no output schema, and 0% schema description coverage, the description is incomplete. It lacks details on behavioral traits, parameter meanings beyond identification, and expected outcomes, making it inadequate for a mutation tool with rich input options.

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

Parameters2/5

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

The schema description coverage is 0%, meaning parameters are undocumented in the schema, and the description only addresses identification ('task_id' or 'task_name') without explaining the 'items' array structure or the many other parameters like 'content', 'priority', or 'due_date'. This leaves most parameters unexplained, and the description does not compensate for the low coverage, providing insufficient semantic context for effective use.

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

Purpose3/5

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

The description states 'Update tasks in Todoist' which provides a clear verb ('Update') and resource ('tasks in Todoist'), making the basic purpose understandable. However, it doesn't differentiate this tool from sibling tools like 'update_projects' or 'update_sections' beyond the resource type, nor does it specify what aspects of tasks can be updated beyond the identification method mentioned.

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

Usage Guidelines2/5

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

The description provides minimal guidance by stating 'Either 'task_id' or the 'task_name' to identify the target,' which hints at how to use the tool for identification but offers no broader context. It doesn't explain when to use this tool versus alternatives like 'create_tasks' or 'delete_tasks,' nor does it mention prerequisites or exclusions, leaving usage unclear beyond basic identification.

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/stanislavlysenko0912/todoist-mcp-server'

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