todoist_task_get
Retrieve Todoist tasks using filters, search terms, or specific criteria like priority, due dates, or project IDs to manage and organize your workflow.
Instructions
Retrieve tasks from Todoist. Use 'filter' for Todoist filter syntax (e.g., 'today', 'p1') or 'task_name' for simple text search in task content
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| task_id | No | Get a specific task by its ID (optional, takes precedence over filtering) | |
| project_id | No | Filter tasks by project ID (optional) | |
| label_id | No | Filter tasks by label ID (optional) | |
| priority | No | Filter tasks by priority level 1 (highest) to 4 (lowest) (optional) | |
| limit | No | Maximum number of tasks to return (optional) | |
| due_before | No | Return only tasks due strictly before this date (YYYY-MM-DD, optional) | |
| due_after | No | Return only tasks due strictly after this date (YYYY-MM-DD, optional) | |
| filter | No | Todoist filter string like 'today', 'overdue', 'p1' (optional) | |
| lang | No | Language for filter parsing, defaults to 'en' (optional) | |
| task_name | No | Filter tasks by name/content using partial text matching (case-insensitive, optional) |
Implementation Reference
- src/handlers/task-handlers.ts:185-372 (handler)Core handler function that executes the todoist_task_get tool logic: validates args, fetches tasks via Todoist API (specific ID or filtered), applies client-side filters, caches results, and returns formatted list.export async function handleGetTasks( todoistClient: TodoistApi, args: GetTasksArgs ): Promise<string> { // Validate input validatePriority(args.priority); validateProjectId(args.project_id); validateLimit(args.limit); if (args.due_before) { validateDateString(args.due_before, "due_before"); } if (args.due_after) { validateDateString(args.due_after, "due_after"); } // If task_id is provided, fetch specific task if (args.task_id) { try { const task = await todoistClient.getTask(args.task_id); return formatTaskForDisplay(task as TodoistTask); } catch { return `Task with ID "${args.task_id}" not found`; } } const filterString = args.filter?.trim(); const language = args.lang?.trim(); const dueBefore = args.due_before?.trim(); const dueAfter = args.due_after?.trim(); let tasks: TodoistTask[] | null = null; if (filterString) { const filterCacheKey = createCacheKey("tasks_filter", { filter: filterString, lang: language, limit: args.limit, }); tasks = taskCache.get(filterCacheKey); if (!tasks) { try { const result = await todoistClient.getTasksByFilter({ query: filterString, lang: language, limit: args.limit, }); tasks = extractArrayFromResponse<TodoistTask>(result); taskCache.set(filterCacheKey, tasks); } catch (error: unknown) { // Check if it's a 400 Bad Request from invalid filter syntax if (error instanceof Error && error.message.includes("400")) { throw new ValidationError( `Invalid filter syntax "${filterString}". The filter parameter expects Todoist filter syntax ` + `like 'today', 'overdue', 'p1', or 'search:"${filterString}"'. ` + `For simple text search, use the task_name parameter instead.`, "filter" ); } // Re-throw other errors throw error; } } } else { const apiParams: Record<string, string | number | undefined> = {}; if (args.project_id) { apiParams.projectId = args.project_id; } if (args.label_id) { apiParams.label = args.label_id; } if (args.limit && args.limit > 0) { apiParams.limit = args.limit; } const cacheKey = createCacheKey("tasks", apiParams); tasks = taskCache.get(cacheKey); if (!tasks) { const result = await todoistClient.getTasks( Object.keys(apiParams).length > 0 ? (apiParams as Parameters<typeof todoistClient.getTasks>[0]) : undefined ); // Handle both array response and object response formats tasks = extractArrayFromResponse<TodoistTask>(result); taskCache.set(cacheKey, tasks); } } let filteredTasks = tasks || []; if (args.project_id) { filteredTasks = filteredTasks.filter( (task) => task.projectId === args.project_id ); } // Handle label filtering - support both IDs and names if (args.label_id) { let labelName = args.label_id; // Remove @ prefix if present if (labelName.startsWith("@")) { labelName = labelName.substring(1); } // Check if it's a numeric ID and resolve to name if (/^\d+$/.test(labelName)) { const labels = await getAllLabels(todoistClient); const label = labels.find((l) => l.id === labelName); labelName = label ? label.name : labelName; } // Filter tasks by label name filteredTasks = filteredTasks.filter((task) => Array.isArray(task.labels) ? task.labels.includes(labelName) : false ); } // Handle @label syntax in filter parameter if (filterString) { const labelMatches = filterString.match(/@([\w-]+)/g); if (labelMatches) { const requiredLabels = labelMatches.map((m) => m.substring(1)); // Check if it's an AND condition (all labels required) if (filterString.includes("&")) { filteredTasks = filteredTasks.filter((task) => { if (!Array.isArray(task.labels)) return false; return requiredLabels.every((label) => task.labels!.includes(label)); }); } else { // OR condition (any label matches) filteredTasks = filteredTasks.filter((task) => { if (!Array.isArray(task.labels)) return false; return requiredLabels.some((label) => task.labels!.includes(label)); }); } } } const apiPriorityFilter = toApiPriority(args.priority); if (apiPriorityFilter !== undefined) { filteredTasks = filteredTasks.filter( (task) => task.priority === apiPriorityFilter ); } if (dueBefore || dueAfter) { filteredTasks = filteredTasks.filter((task) => { const dueDate = getDueDateOnly(task.due); if (!dueDate) { return false; } const isBeforeThreshold = !dueBefore || dueDate < dueBefore; const isAfterThreshold = !dueAfter || dueDate > dueAfter; return isBeforeThreshold && isAfterThreshold; }); } // Apply task_name filter if provided if (args.task_name) { const searchTerm = args.task_name.toLowerCase(); filteredTasks = filteredTasks.filter((task) => task.content.toLowerCase().includes(searchTerm) ); } if (args.limit && args.limit > 0) { filteredTasks = filteredTasks.slice(0, args.limit); } const taskList = filteredTasks .map((task) => formatTaskForDisplay(task)) .join("\n\n"); const taskCount = filteredTasks.length; if (taskCount === 0) { return "No tasks found matching the criteria"; } const taskWord = taskCount === 1 ? "task" : "tasks"; return `${taskCount} ${taskWord} found:\n\n${taskList}`; }
- src/tools/task-tools.ts:55-113 (schema)Tool definition including name, description, and full inputSchema for validating arguments to todoist_task_get.export const GET_TASKS_TOOL: Tool = { name: "todoist_task_get", description: "Retrieve tasks from Todoist. Use 'filter' for Todoist filter syntax (e.g., 'today', 'p1') or 'task_name' for simple text search in task content", inputSchema: { type: "object", properties: { task_id: { type: "string", description: "Get a specific task by its ID (optional, takes precedence over filtering)", }, project_id: { type: "string", description: "Filter tasks by project ID (optional)", }, label_id: { type: "string", description: "Filter tasks by label ID (optional)", }, priority: { type: "number", description: "Filter tasks by priority level 1 (highest) to 4 (lowest) (optional)", enum: [1, 2, 3, 4], }, limit: { type: "number", description: "Maximum number of tasks to return (optional)", minimum: 1, }, due_before: { type: "string", description: "Return only tasks due strictly before this date (YYYY-MM-DD, optional)", }, due_after: { type: "string", description: "Return only tasks due strictly after this date (YYYY-MM-DD, optional)", }, filter: { type: "string", description: "Todoist filter string like 'today', 'overdue', 'p1' (optional)", }, lang: { type: "string", description: "Language for filter parsing, defaults to 'en' (optional)", }, task_name: { type: "string", description: "Filter tasks by name/content using partial text matching (case-insensitive, optional)", }, }, required: [], }, };
- src/index.ts:156-161 (registration)Dispatch registration in main server CallToolRequest handler: validates args with type guard and calls the task handler function.case "todoist_task_get": if (!isGetTasksArgs(args)) { throw new Error("Invalid arguments for todoist_task_get"); } result = await handleGetTasks(apiClient, args); break;
- src/type-guards.ts:33-50 (schema)Type guard function isGetTasksArgs used for runtime validation of arguments against the tool schema before calling handler.export function isGetTasksArgs(args: unknown): args is GetTasksArgs { if (typeof args !== "object" || args === null) return false; const obj = args as Record<string, unknown>; return ( (obj.project_id === undefined || typeof obj.project_id === "string") && (obj.filter === undefined || typeof obj.filter === "string") && (obj.label_id === undefined || typeof obj.label_id === "string") && (obj.priority === undefined || typeof obj.priority === "number") && (obj.limit === undefined || typeof obj.limit === "number") && (obj.due_before === undefined || typeof obj.due_before === "string") && (obj.due_after === undefined || typeof obj.due_after === "string") && (obj.lang === undefined || typeof obj.lang === "string") && (obj.task_name === undefined || typeof obj.task_name === "string") ); } export function isUpdateTaskArgs(args: unknown): args is UpdateTaskArgs {
- src/tools/index.ts:62-69 (registration)Central tool registry includes todoist_task_get via TASK_TOOLS array, exported as ALL_TOOLS for server listTools handler.export const ALL_TOOLS = [ ...TASK_TOOLS, ...PROJECT_TOOLS, ...COMMENT_TOOLS, ...LABEL_TOOLS, ...SUBTASK_TOOLS, ...TEST_TOOLS, ];