Skip to main content
Glama

delete_projects

Remove projects from Todoist by specifying their ID or name to clean up your workspace and manage task organization.

Instructions

Delete projects from Todoist Either 'id' or the 'name' to identify the target.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
itemsYes

Implementation Reference

  • Core handler logic for batch delete_projects tool: fetches projects list if name lookup needed, resolves project ID by name, validates and encodes ID for path '/projects/{id}', executes todoistApi.delete(finalPath), returns batched results with success 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, }; };
  • Tool registration for 'delete_projects': configures batch handler with schema (id or name), DELETE method to '/projects/{id}', delete mode, name-based lookup.
    createBatchApiHandler({ name: 'delete_projects', description: 'Delete projects from Todoist', itemSchema: { id: z.string().optional().describe('ID of the project to delete (preferred over name)'), name: z.string().optional().describe('Name of the project to delete'), }, method: 'DELETE', path: '/projects/{id}', mode: 'delete', idField: 'id', nameField: 'name', findByName: (name, items) => items.find(item => item.name.toLowerCase().includes(name.toLowerCase())), });
  • Input schema for delete_projects: optional project id (preferred) or name.
    itemSchema: { id: z.string().optional().describe('ID of the project to delete (preferred over name)'), name: z.string().optional().describe('Name of the project to delete'), },
  • createHandler helper: wraps the tool handler, handles errors, formats output, and registers the tool with MCP server.tool(name, description, schema, callback). Called by createBatchApiHandler.
    export function createHandler<T extends z.ZodRawShape>( name: string, description: string, paramsSchema: T, handler: (args: HandlerArgs<T>) => Promise<any> ): void { const mcpToolCallback = async (args: HandlerArgs<T>): Promise<CallToolResult> => { try { const result = await handler(args); return { content: [ { type: 'text', text: JSON.stringify(result ?? null, null, 2).trim(), }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error in tool ${name}:`, error); return { isError: true, content: [ { type: 'text', text: `Error executing tool '${name}': ${errorMessage}`, }, ], }; } }; // Crazy cast, if you can do it better, please, let me knows server.tool(name, description, paramsSchema, mcpToolCallback as unknown as ToolCallback<T>); }
  • createBatchApiHandler calls createHandler to register the generated batch handler for delete_projects.
    return createHandler(options.name, finalDescription, batchSchema.shape, handler); }

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