Skip to main content
Glama
cristip73

MCP Server for Asana

by cristip73

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
NameRequiredDescriptionDefault
workspaceNoThe workspace to search in (optional if DEFAULT_WORKSPACE_ID is set)
textNoText to search for in task names and descriptions
resource_subtypeNoFilter by task subtype (e.g. milestone)
portfolios_anyNoComma-separated list of portfolio IDs
assignee_anyNoComma-separated list of user IDs
assignee_notNoComma-separated list of user IDs to exclude
projects_anyNoComma-separated list of project IDs
projects_notNoComma-separated list of project IDs to exclude
projects_allNoComma-separated list of project IDs that must all match
sections_anyNoComma-separated list of section IDs
sections_notNoComma-separated list of section IDs to exclude
sections_allNoComma-separated list of section IDs that must all match
tags_anyNoComma-separated list of tag IDs
tags_notNoComma-separated list of tag IDs to exclude
tags_allNoComma-separated list of tag IDs that must all match
teams_anyNoComma-separated list of team IDs
followers_notNoComma-separated list of user IDs to exclude
created_by_anyNoComma-separated list of user IDs
created_by_notNoComma-separated list of user IDs to exclude
assigned_by_anyNoComma-separated list of user IDs
assigned_by_notNoComma-separated list of user IDs to exclude
liked_by_notNoComma-separated list of user IDs to exclude
commented_on_by_notNoComma-separated list of user IDs to exclude
due_onNoISO 8601 date string or null
due_on_beforeNoISO 8601 date string
due_on_afterNoISO 8601 date string
due_at_beforeNoISO 8601 datetime string
due_at_afterNoISO 8601 datetime string
start_onNoISO 8601 date string or null
start_on_beforeNoISO 8601 date string
start_on_afterNoISO 8601 date string
created_onNoISO 8601 date string or null
created_on_beforeNoISO 8601 date string
created_on_afterNoISO 8601 date string
created_at_beforeNoISO 8601 datetime string
created_at_afterNoISO 8601 datetime string
completed_onNoISO 8601 date string or null
completed_on_beforeNoISO 8601 date string
completed_on_afterNoISO 8601 date string
completed_at_beforeNoISO 8601 datetime string
completed_at_afterNoISO 8601 datetime string
modified_onNoISO 8601 date string or null
modified_on_beforeNoISO 8601 date string
modified_on_afterNoISO 8601 date string
modified_at_beforeNoISO 8601 datetime string
modified_at_afterNoISO 8601 datetime string
completedNoFilter for completed tasks
is_subtaskNoFilter for subtasks
has_attachmentNoFilter for tasks with attachments
is_blockedNoFilter for tasks with incomplete dependencies
is_blockingNoFilter for incomplete tasks with dependents
sort_byNoSort by: due_date, created_at, completed_at, likes, modified_atmodified_at
sort_ascendingNoSort in ascending order
opt_fieldsNoComma-separated list of optional fields to include
custom_fieldsNoObject 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

  • 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;
      }
    }
  • 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: []
      }
    };
  • 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) }],
      };
    }
  • Registration of searchTasksTool in the main tools export array used by MCP.
    searchTasksTool,
  • 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;
      }
    }

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/cristip73/mcp-server-asana'

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