todoist_search_tasks
Search for specific tasks in Todoist by content or name using a query, optionally filtered by project and limited by result count, when task IDs are unknown.
Instructions
Search for tasks by content/name (fallback for when ID is not known)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default: 10) (optional) | |
| projectId | No | Limit search to specific project (optional) | |
| query | Yes | Search query to find tasks by content |
Implementation Reference
- src/index.ts:1299-1354 (handler)The handler function for the 'todoist_search_tasks' tool. It validates the input arguments using isSearchTasksArgs, constructs a search query for Todoist's getTasksByFilter API (prefixing with 'search: ' if needed), retrieves matching tasks, optionally fetches project names for better output, formats the results using formatTask, and returns a detailed text response with pagination info if available.if (name === "todoist_search_tasks") { if (!isSearchTasksArgs(args)) { throw new Error("Invalid arguments for todoist_search_tasks"); } // Prepare arguments for getTasksByFilter // Prepend "search: " to the query for more robust keyword searching with Todoist API const searchQuery = args.query.startsWith("search:") ? args.query : `search: ${args.query}`; const filterArgs: any = { query: searchQuery }; if (args.limit) filterArgs.limit = args.limit; // Note: args.projectId is not directly used by getTasksByFilter unless incorporated into the query string. // For example: `search: ${args.query} & #ProjectName` or `search: ${args.query} & ##ProjectID` const tasksResponse = await todoistClient.getTasksByFilter(filterArgs); const matchingTasksData = tasksResponse.results || []; if (matchingTasksData.length === 0) { return { content: [{ type: "text", text: `No tasks found matching the filter query "${args.query}"` }], isError: false, }; } // Asynchronously format tasks and fetch project names if necessary const formattedTaskList = await Promise.all(matchingTasksData.map(async (task: any) => { let taskDisplay = formatTask(task); // formatTask now includes Project ID if (task.projectId) { try { const project = await todoistClient.getProject(task.projectId); taskDisplay += `\n Project Name: ${project.name}`; } catch (projectError: any) { // Silently ignore project fetch errors for search, or log them // taskDisplay += `\n Project Name: (Error fetching project details)`; console.error(`Error fetching project ${task.projectId} for search result: ${projectError.message}`); } } return taskDisplay; })); const taskListString = formattedTaskList.join('\n\n'); const nextCursorMessage = tasksResponse.nextCursor ? `\n\nNext cursor for more results: ${tasksResponse.nextCursor}` : ''; return { content: [{ type: "text", text: `Found ${matchingTasksData.length} task(s) matching "${args.query}":\n\n${taskListString}${nextCursorMessage}` }], isError: false, }; }
- src/index.ts:559-580 (schema)The Tool object definition for 'todoist_search_tasks', including the name, description, and inputSchema for validation (query required, optional projectId and limit).name: "todoist_search_tasks", description: "Search for tasks by content/name (fallback for when ID is not known)", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query to find tasks by content" }, projectId: { type: "string", description: "Limit search to specific project (optional)" }, limit: { type: "number", description: "Maximum number of results (default: 10) (optional)", default: 10 } }, required: ["query"] } };
- src/index.ts:1083-1121 (registration)The ListToolsRequestSchema handler where SEARCH_TASKS_TOOL is registered by including it in the returned tools array.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ // Task tools CREATE_TASK_TOOL, QUICK_ADD_TASK_TOOL, GET_TASKS_TOOL, GET_TASK_TOOL, UPDATE_TASK_TOOL, DELETE_TASK_TOOL, COMPLETE_TASK_TOOL, REOPEN_TASK_TOOL, SEARCH_TASKS_TOOL, MOVE_TASK_TOOL, BULK_MOVE_TASKS_TOOL, // Project tools GET_PROJECTS_TOOL, GET_PROJECT_TOOL, CREATE_PROJECT_TOOL, UPDATE_PROJECT_TOOL, DELETE_PROJECT_TOOL, // Section tools GET_SECTIONS_TOOL, CREATE_SECTION_TOOL, UPDATE_SECTION_TOOL, DELETE_SECTION_TOOL, // Label tools CREATE_LABEL_TOOL, GET_LABEL_TOOL, GET_LABELS_TOOL, UPDATE_LABEL_TOOL, DELETE_LABEL_TOOL, // Comment tools CREATE_COMMENT_TOOL, GET_COMMENT_TOOL, GET_COMMENTS_TOOL, UPDATE_COMMENT_TOOL, DELETE_COMMENT_TOOL, ], }));
- src/index.ts:913-924 (helper)Type guard function 'isSearchTasksArgs' used in the handler to validate and type-check the input arguments for the todoist_search_tasks tool.function isSearchTasksArgs(args: unknown): args is { query: string; projectId?: string; limit?: number; } { return ( typeof args === "object" && args !== null && "query" in args && typeof (args as { query: string }).query === "string" ); }
- src/index.ts:705-719 (helper)Helper function 'formatTask' used by the handler to format individual task details into a readable string for the response.function formatTask(task: any): string { let taskDetails = `- ID: ${task.id}\n Content: ${task.content}`; if (task.description) taskDetails += `\n Description: ${task.description}`; if (task.due) taskDetails += `\n Due: ${task.due.string}`; if (task.priority && task.priority > 1) taskDetails += `\n Priority: ${task.priority}`; if (task.labels && task.labels.length > 0) taskDetails += `\n Labels: ${task.labels.join(', ')}`; if (task.projectId) taskDetails += `\n Project ID: ${task.projectId}`; if (task.sectionId) taskDetails += `\n Section ID: ${task.sectionId}`; if (task.parentId) taskDetails += `\n Parent ID: ${task.parentId}`; if (task.url) taskDetails += `\n URL: ${task.url}`; if (task.commentCount > 0) taskDetails += `\n Comments: ${task.commentCount}`; if (task.createdAt) taskDetails += `\n Created At: ${task.createdAt}`; if (task.creatorId) taskDetails += `\n Creator ID: ${task.creatorId}`; return taskDetails; }