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;
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions 'advanced filtering options' but doesn't describe key behaviors like pagination, rate limits, authentication requirements, error handling, or what the output looks like. For a search tool with 55 parameters and no annotations, this leaves significant gaps in understanding how the tool operates.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without unnecessary words. It's appropriately sized and front-loaded, making it easy to understand at a glance while leaving detailed parameter info to the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (55 parameters, no annotations, no output schema), the description is inadequate. It doesn't address behavioral aspects like result format, pagination, or error conditions, nor does it provide usage context relative to sibling tools. For a search tool with extensive filtering options, more guidance on how to effectively use the parameters would be beneficial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with detailed descriptions for all 55 parameters in the input schema. The description adds minimal value beyond this, only implying filtering capabilities without explaining parameter interactions or usage patterns. With high schema coverage, the baseline is 3, as the schema does the heavy lifting for parameter documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Search') and resource ('tasks in a workspace'), specifying the action and target. It also mentions 'advanced filtering options' which adds context about functionality. However, it doesn't explicitly differentiate from sibling tools like 'asana_get_tasks_for_project' or 'asana_search_projects' beyond the general search nature.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With many sibling tools for retrieving tasks (e.g., 'asana_get_tasks_for_project', 'asana_get_task', 'asana_search_projects'), there's no indication of when this search tool is preferred over those, nor any prerequisites or exclusions mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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