Skip to main content
Glama
vitalio-sh

Enhanced Todoist MCP Server Extended

todoist_search_tasks

Search for Todoist tasks by content or name when you don't know the task ID. Filter results by project and limit the number of matches.

Instructions

Search for tasks by content/name (fallback for when ID is not known)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query to find tasks by content
projectIdNoLimit search to specific project (optional)
limitNoMaximum number of results (default: 10) (optional)

Implementation Reference

  • The core handler logic for executing the todoist_search_tasks tool. It validates arguments, constructs a Todoist filter query prefixed with 'search:', calls todoistClient.getTasksByFilter, fetches project details for each task, formats the output using formatTask, and returns a rich 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,
      };
    }
  • JSON schema definition for the todoist_search_tasks tool, specifying input parameters: required 'query' string, optional 'projectId' and 'limit'.
    const SEARCH_TASKS_TOOL: Tool = {
      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)
    Registration of the todoist_search_tasks tool (as SEARCH_TASKS_TOOL) in the list of available tools returned by the ListToolsRequestSchema handler.
    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,
      ],
    }));
  • Type guard helper function used to validate the input arguments for the todoist_search_tasks handler.
    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"
      );
    }
Behavior2/5

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

No annotations are provided, so the description carries full burden. It mentions searching by content/name, which is useful, but lacks details on behavioral traits like pagination, rate limits, authentication needs, or what the search returns (e.g., partial matches, case sensitivity). For a search tool with no annotations, this is a significant gap.

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 front-loads the core purpose ('Search for tasks by content/name') and adds context ('fallback for when ID is not known') without waste. Every word earns its place.

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

Completeness3/5

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

Given no annotations, no output schema, and 3 parameters with full schema coverage, the description is minimally adequate. It covers the basic purpose and usage context but lacks behavioral details (e.g., return format, error handling) that would be helpful for a search operation, leaving gaps in completeness.

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%, so the schema already documents all parameters (query, projectId, limit). The description adds minimal value beyond the schema by implying the search is based on content/name, but doesn't provide additional syntax, format, or usage details for parameters.

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'), specifying it searches by content/name. It distinguishes from ID-based lookup by mentioning 'fallback for when ID is not known', but doesn't explicitly differentiate from other search-like siblings like 'todoist_get_tasks'.

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

Usage Guidelines4/5

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

The description provides clear context for usage ('fallback for when ID is not known'), which implicitly suggests using this when task IDs are unavailable. However, it doesn't explicitly state when not to use it or name alternatives among siblings (e.g., 'todoist_get_tasks' might also retrieve tasks).

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/vitalio-sh/todoist-mcp-server-ext'

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