asana_search_tasks
Search tasks in Asana workspaces using text queries and advanced filters for assignees, projects, tags, dates, custom fields, and completion status.
Instructions
Search tasks in a workspace with advanced filtering options
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| workspace | No | The workspace to search in (optional if DEFAULT_WORKSPACE_ID is set) | |
| text | No | Text to search for in task names and descriptions | |
| resource_subtype | No | Filter by task subtype (e.g. milestone) | |
| portfolios_any | No | Comma-separated list of portfolio IDs | |
| assignee_any | No | Comma-separated list of user IDs | |
| assignee_not | No | Comma-separated list of user IDs to exclude | |
| projects_any | No | Comma-separated list of project IDs | |
| projects_not | No | Comma-separated list of project IDs to exclude | |
| projects_all | No | Comma-separated list of project IDs that must all match | |
| sections_any | No | Comma-separated list of section IDs | |
| sections_not | No | Comma-separated list of section IDs to exclude | |
| sections_all | No | Comma-separated list of section IDs that must all match | |
| tags_any | No | Comma-separated list of tag IDs | |
| tags_not | No | Comma-separated list of tag IDs to exclude | |
| tags_all | No | Comma-separated list of tag IDs that must all match | |
| teams_any | No | Comma-separated list of team IDs | |
| followers_not | No | Comma-separated list of user IDs to exclude | |
| created_by_any | No | Comma-separated list of user IDs | |
| created_by_not | No | Comma-separated list of user IDs to exclude | |
| assigned_by_any | No | Comma-separated list of user IDs | |
| assigned_by_not | No | Comma-separated list of user IDs to exclude | |
| liked_by_not | No | Comma-separated list of user IDs to exclude | |
| commented_on_by_not | No | Comma-separated list of user IDs to exclude | |
| due_on | No | ISO 8601 date string or null | |
| due_on_before | No | ISO 8601 date string | |
| due_on_after | No | ISO 8601 date string | |
| due_at_before | No | ISO 8601 datetime string | |
| due_at_after | No | ISO 8601 datetime string | |
| start_on | No | ISO 8601 date string or null | |
| start_on_before | No | ISO 8601 date string | |
| start_on_after | No | ISO 8601 date string | |
| created_on | No | ISO 8601 date string or null | |
| created_on_before | No | ISO 8601 date string | |
| created_on_after | No | ISO 8601 date string | |
| created_at_before | No | ISO 8601 datetime string | |
| created_at_after | No | ISO 8601 datetime string | |
| completed_on | No | ISO 8601 date string or null | |
| completed_on_before | No | ISO 8601 date string | |
| completed_on_after | No | ISO 8601 date string | |
| completed_at_before | No | ISO 8601 datetime string | |
| completed_at_after | No | ISO 8601 datetime string | |
| modified_on | No | ISO 8601 date string or null | |
| modified_on_before | No | ISO 8601 date string | |
| modified_on_after | No | ISO 8601 date string | |
| modified_at_before | No | ISO 8601 datetime string | |
| modified_at_after | No | ISO 8601 datetime string | |
| completed | No | Filter for completed tasks | |
| is_subtask | No | Filter for subtasks | |
| has_attachment | No | Filter for tasks with attachments | |
| is_blocked | No | Filter for tasks with incomplete dependencies | |
| is_blocking | No | Filter for incomplete tasks with dependents | |
| sort_by | No | Sort by: due_date, created_at, completed_at, likes, modified_at | modified_at |
| sort_ascending | No | Sort in ascending order | |
| opt_fields | No | Comma-separated list of optional fields to include | |
| custom_fields | No | Object containing custom field filters. Keys should be in the format "{gid}.{operation}" where operation can be: - {gid}.is_set: Boolean - For all custom field types, check if value is set - {gid}.value: String|Number|String(enum_option_gid) - Direct value match for Text, Number or Enum fields - {gid}.starts_with: String - For Text fields only, check if value starts with string - {gid}.ends_with: String - For Text fields only, check if value ends with string - {gid}.contains: String - For Text fields only, check if value contains string - {gid}.less_than: Number - For Number fields only, check if value is less than number - {gid}.greater_than: Number - For Number fields only, check if value is greater than number Example: { "12345.value": "high", "67890.contains": "urgent" } |
Implementation Reference
- src/asana-client-wrapper.ts:200-325 (handler)Core handler implementation for asana_search_tasks. Processes input parameters, handles custom fields and pagination, calls Asana API searchTasksForWorkspace, transforms response with custom field display values.async searchTasks(workspace: string | undefined, searchOpts: any = {}) { try { // Use default workspace if not specified and available if (!workspace && this.defaultWorkspaceId) { workspace = this.defaultWorkspaceId; } if (!workspace) { throw new Error("No workspace specified and no default workspace ID set"); } // Extract pagination parameters const { auto_paginate = false, max_pages = 10, limit, offset, // Extract other known parameters text, resource_subtype, completed, is_subtask, has_attachment, is_blocked, is_blocking, sort_by, sort_ascending, opt_fields, ...otherOpts } = searchOpts; // Build search parameters const searchParams: any = { ...otherOpts // Include any additional filter parameters }; // Handle custom fields if provided if (searchOpts.custom_fields) { if (typeof searchOpts.custom_fields == "string") { try { searchOpts.custom_fields = JSON.parse(searchOpts.custom_fields); } catch (err) { if (err instanceof Error) { err.message = "custom_fields must be a JSON object : " + err.message; } throw err; } } Object.entries(searchOpts.custom_fields).forEach(([key, value]) => { searchParams[`custom_fields.${key}`] = value; }); delete searchParams.custom_fields; // Remove the custom_fields object since we've processed it } // Add optional parameters if provided if (text) searchParams.text = text; if (resource_subtype) searchParams.resource_subtype = resource_subtype; if (completed !== undefined) searchParams.completed = completed; if (is_subtask !== undefined) searchParams.is_subtask = is_subtask; if (has_attachment !== undefined) searchParams.has_attachment = has_attachment; if (is_blocked !== undefined) searchParams.is_blocked = is_blocked; if (is_blocking !== undefined) searchParams.is_blocking = is_blocking; if (sort_by) searchParams.sort_by = sort_by; if (sort_ascending !== undefined) searchParams.sort_ascending = sort_ascending; if (opt_fields) searchParams.opt_fields = opt_fields; // Add pagination parameters if (limit !== undefined) { // Ensure limit is between 1 and 100 searchParams.limit = Math.min(Math.max(1, Number(limit)), 100); } if (offset) searchParams.offset = offset; // Use the paginated results handler for more reliable pagination const results = await this.handlePaginatedResults( // Initial fetch function () => this.tasks.searchTasksForWorkspace(workspace, searchParams), // Next page fetch function (nextOffset) => this.tasks.searchTasksForWorkspace(workspace, { ...searchParams, offset: nextOffset }), // Pagination options { auto_paginate, max_pages } ); // Transform the response to simplify custom fields if present return results.map((task: any) => { if (!task.custom_fields) return task; return { ...task, custom_fields: task.custom_fields.reduce((acc: any, field: any) => { const key = `${field.name} (${field.gid})`; let value = field.display_value; // For enum fields with a value, include the enum option GID if (field.type === 'enum' && field.enum_value) { value = `${field.display_value} (${field.enum_value.gid})`; } acc[key] = value; return acc; }, {}) }; }); } catch (error: any) { console.error(`Error searching tasks: ${error.message}`); // Add more detailed error handling for common issues if (error.message?.includes('Bad Request')) { // Check for common causes of Bad Request if (searchOpts.projects_any) { throw new Error(`Error searching tasks with projects_any: ${error.message}. Try using 'projects.any' instead of 'projects_any', or use getTasksForProject directly.`); } if (searchOpts.limit && (searchOpts.limit < 1 || searchOpts.limit > 100)) { throw new Error(`Invalid limit parameter: ${searchOpts.limit}. Limit must be between 1 and 100.`); } if (searchOpts.offset && !searchOpts.offset.startsWith('eyJ')) { throw new Error(`Invalid offset parameter: ${searchOpts.offset}. Offset must be a valid pagination token from a previous response.`); } // Generic bad request error with suggestions throw new Error(`Bad Request error when searching tasks. Check that all parameters are valid. Common issues: invalid workspace ID, invalid project reference, or incompatible search filters. ${error.message}`); } throw error; } }
- src/tools/task-tools.ts:3-242 (schema)Tool schema definition including name, description, and comprehensive inputSchema with all search filters like text, dates, assignees, projects, custom_fields, pagination options.export const searchTasksTool: Tool = { name: "asana_search_tasks", description: "Search tasks in a workspace with advanced filtering options", inputSchema: { type: "object", properties: { workspace: { type: "string", description: "The workspace to search in (optional if DEFAULT_WORKSPACE_ID is set)" }, text: { type: "string", description: "Text to search for in task names and descriptions" }, resource_subtype: { type: "string", description: "Filter by task subtype (e.g. milestone)" }, "portfolios_any": { type: "string", description: "Comma-separated list of portfolio IDs" }, "assignee_any": { type: "string", description: "Comma-separated list of user IDs" }, "assignee_not": { type: "string", description: "Comma-separated list of user IDs to exclude" }, "projects_any": { type: "string", description: "Comma-separated list of project IDs" }, "projects_not": { type: "string", description: "Comma-separated list of project IDs to exclude" }, "projects_all": { type: "string", description: "Comma-separated list of project IDs that must all match" }, "sections_any": { type: "string", description: "Comma-separated list of section IDs" }, "sections_not": { type: "string", description: "Comma-separated list of section IDs to exclude" }, "sections_all": { type: "string", description: "Comma-separated list of section IDs that must all match" }, "tags_any": { type: "string", description: "Comma-separated list of tag IDs" }, "tags_not": { type: "string", description: "Comma-separated list of tag IDs to exclude" }, "tags_all": { type: "string", description: "Comma-separated list of tag IDs that must all match" }, "teams_any": { type: "string", description: "Comma-separated list of team IDs" }, "followers_not": { type: "string", description: "Comma-separated list of user IDs to exclude" }, "created_by_any": { type: "string", description: "Comma-separated list of user IDs" }, "created_by_not": { type: "string", description: "Comma-separated list of user IDs to exclude" }, "assigned_by_any": { type: "string", description: "Comma-separated list of user IDs" }, "assigned_by_not": { type: "string", description: "Comma-separated list of user IDs to exclude" }, "liked_by_not": { type: "string", description: "Comma-separated list of user IDs to exclude" }, "commented_on_by_not": { type: "string", description: "Comma-separated list of user IDs to exclude" }, "due_on": { type: "string", description: "ISO 8601 date string or null" }, "due_on_before": { type: "string", description: "ISO 8601 date string" }, "due_on_after": { type: "string", description: "ISO 8601 date string" }, "due_at_before": { type: "string", description: "ISO 8601 datetime string" }, "due_at_after": { type: "string", description: "ISO 8601 datetime string" }, "start_on": { type: "string", description: "ISO 8601 date string or null" }, "start_on_before": { type: "string", description: "ISO 8601 date string" }, "start_on_after": { type: "string", description: "ISO 8601 date string" }, "created_on": { type: "string", description: "ISO 8601 date string or null" }, "created_on_before": { type: "string", description: "ISO 8601 date string" }, "created_on_after": { type: "string", description: "ISO 8601 date string" }, "created_at_before": { type: "string", description: "ISO 8601 datetime string" }, "created_at_after": { type: "string", description: "ISO 8601 datetime string" }, "completed_on": { type: "string", description: "ISO 8601 date string or null"}, "completed_on_before": { type: "string", description: "ISO 8601 date string" }, "completed_on_after": { type: "string", description: "ISO 8601 date string" }, "completed_at_before": { type: "string", description: "ISO 8601 datetime string" }, "completed_at_after": { type: "string", description: "ISO 8601 datetime string" }, "modified_on": { type: "string", description: "ISO 8601 date string or null" }, "modified_on_before": { type: "string", description: "ISO 8601 date string" }, "modified_on_after": { type: "string", description: "ISO 8601 date string" }, "modified_at_before": { type: "string", description: "ISO 8601 datetime string" }, "modified_at_after": { type: "string", description: "ISO 8601 datetime string" }, completed: { type: "boolean", description: "Filter for completed tasks" }, is_subtask: { type: "boolean", description: "Filter for subtasks" }, has_attachment: { type: "boolean", description: "Filter for tasks with attachments" }, is_blocked: { type: "boolean", description: "Filter for tasks with incomplete dependencies" }, is_blocking: { type: "boolean", description: "Filter for incomplete tasks with dependents" }, sort_by: { type: "string", description: "Sort by: due_date, created_at, completed_at, likes, modified_at", default: "modified_at" }, sort_ascending: { type: "boolean", description: "Sort in ascending order", default: false }, opt_fields: { type: "string", description: "Comma-separated list of optional fields to include" }, custom_fields: { type: "object", description: `Object containing custom field filters. Keys should be in the format "{gid}.{operation}" where operation can be: - {gid}.is_set: Boolean - For all custom field types, check if value is set - {gid}.value: String|Number|String(enum_option_gid) - Direct value match for Text, Number or Enum fields - {gid}.starts_with: String - For Text fields only, check if value starts with string - {gid}.ends_with: String - For Text fields only, check if value ends with string - {gid}.contains: String - For Text fields only, check if value contains string - {gid}.less_than: Number - For Number fields only, check if value is less than number - {gid}.greater_than: Number - For Number fields only, check if value is greater than number Example: { "12345.value": "high", "67890.contains": "urgent" }` } }, required: [] } };
- src/tool-handler.ts:158-164 (registration)Dispatch handler in tool_handler switch statement that extracts arguments and calls AsanaClientWrapper.searchTasks method.case "asana_search_tasks": { const { workspace, ...searchOpts } = args; const response = await asanaClient.searchTasks(workspace || undefined, searchOpts); return { content: [{ type: "text", text: JSON.stringify(response) }], }; }
- src/tool-handler.ts:75-75 (registration)Registration of searchTasksTool in the main tools export array used by MCP.searchTasksTool,
- src/asana-client-wrapper.ts:53-68 (helper)Helper method used by searchTasks for automatic pagination handling across multiple pages.private async handlePaginatedResults<T>( initialFetch: () => Promise<PaginatedResponse<T>>, nextPageFetch: (offset: string) => Promise<PaginatedResponse<T>>, options: PaginationOptions = {} ): Promise<T[]> { try { // Fetch the initial page const initialResponse = await initialFetch(); // Use the pagination utility to handle additional pages if needed return await handlePagination(initialResponse, nextPageFetch, options); } catch (error: any) { console.error(`Error in paginated request: ${error.message}`); throw error; } }