Skip to main content
Glama
mikemc

Todoist MCP Server

by mikemc

todoist_get_tasks

Retrieve filtered Todoist tasks by project ID, priority, natural language filters, or task limits to organize and manage your to-do list effectively.

Instructions

Get a list of tasks from Todoist with various filters

Args: project_id: Filter tasks by project ID (optional) filter: Natural language filter like 'today', 'tomorrow', 'next week', 'priority 1', 'overdue' (optional) priority: Filter by priority level (1-4) (optional) limit: Maximum number of tasks to return (optional)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filterNo
limitNo
priorityNo
project_idNo

Implementation Reference

  • Main handler function implementing the todoist_get_tasks tool. Fetches tasks from Todoist API supporting filters by project, section, parent, label, or IDs, with pagination handling up to nmax tasks.
    def todoist_get_tasks(
        ctx: Context,
        project_id: Optional[str] = None,
        section_id: Optional[str] = None,
        parent_id: Optional[str] = None,
        label: Optional[str] = None,
        ids: Optional[list[str]] = None,
        nmax: Optional[int] = 100,
        limit: int = 200
    ) -> str:
        """Get a list of tasks from Todoist with basic filters
    
        This is a wrapper around the Todoist API's get_tasks method that handles pagination
        automatically. By default, it will fetch up to 100 matching tasks. Set nmax=None
        to fetch ALL matching tasks across multiple API calls.
    
        For natural language filtering (like 'today', 'overdue'), use todoist_filter_tasks instead.
    
        Examples:
            # Get up to 100 tasks (default)
            todoist_get_tasks(ctx)
    
            # Get all tasks in a project (up to 100 by default)
            todoist_get_tasks(ctx, project_id="12345")
    
            # Get first 500 tasks total
            todoist_get_tasks(ctx, nmax=500)
    
            # Get ALL tasks (unlimited)
            todoist_get_tasks(ctx, nmax=None)
    
            # Get specific tasks by ID
            todoist_get_tasks(ctx, ids=["task1", "task2", "task3"])
    
        Args:
            project_id: Filter tasks by project ID (optional)
            section_id: Filter tasks by section ID (optional)
            parent_id: Filter tasks by parent task ID (optional)
            label: Filter tasks by label name (optional)
            ids: A list of the IDs of the tasks to retrieve (optional)
            nmax: Maximum total number of tasks to return. Set to None for ALL matching tasks (default: 100)
            limit: Number of tasks to fetch per API request (default: 200, max: 200)
        """
        todoist_client = ctx.request_context.lifespan_context.todoist_client
    
        try:
            logger.info(f"Getting tasks with project_id: {project_id}, section_id: {section_id}, parent_id: {parent_id}, label: {label}, nmax: {nmax}, limit: {limit}")
    
            # Early exit for zero requests to avoid unnecessary API calls
            if nmax is not None:
                if nmax == 0:
                    logger.info("nmax=0 specified, returning empty result")
                    return []
                elif nmax < 0:
                    logger.warning(f"Invalid nmax {nmax}, using default of 100")
                    nmax = 100
    
            if limit > 200:
                logger.warning(f"Limit {limit} exceeds API maximum of 200, using 200 instead")
                limit = 200
            elif limit <= 0:
                logger.warning(f"Invalid limit {limit}, using default of 200")
                limit = 200
    
            # Key optimization: match page size to actual need to reduce API payload
            effective_limit = limit
            if nmax is not None and nmax < limit:
                effective_limit = nmax
                logger.info(f"Optimized limit from {limit} to {effective_limit} to match nmax")
    
            params = {}
            if project_id:
                params["project_id"] = project_id
            if section_id:
                params["section_id"] = section_id
            if parent_id:
                params["parent_id"] = parent_id
            if label:
                params["label"] = label
            if ids:
                params["ids"] = ids
            params["limit"] = effective_limit
    
            tasks_iterator = todoist_client.get_tasks(**params)
            all_tasks = []
            pages_fetched = 0
    
            for task_batch in tasks_iterator:
                pages_fetched += 1
                all_tasks.extend(task_batch)
    
                logger.info(f"Fetched page {pages_fetched} with {len(task_batch)} tasks (total: {len(all_tasks)})")
    
                if nmax is not None and len(all_tasks) >= nmax:
                    # Trim excess - rare due to effective_limit optimization, but handles edge cases
                    all_tasks = all_tasks[:nmax]
                    logger.info(f"Reached nmax of {nmax} tasks, stopping pagination")
                    break
    
                # Todoist API signals end of results by returning fewer items than requested
                if len(task_batch) < effective_limit:
                    logger.info(f"Received {len(task_batch)} tasks (less than limit {effective_limit}), reached end of results")
                    break
    
            if not all_tasks:
                logger.info("No tasks found matching the criteria")
                return "No tasks found matching the criteria"
    
            logger.info(f"Retrieved {len(all_tasks)} tasks total across {pages_fetched} pages")
    
            if nmax is None:
                logger.info("Fetched ALL matching tasks (nmax=None specified)")
            elif len(all_tasks) == nmax:
                logger.info(f"Retrieved exactly the requested {nmax} tasks")
    
            return json.dumps([task.to_dict() for task in all_tasks], indent=2, default=str)
    
        except Exception as error:
            logger.error(f"Error getting tasks: {error}")
            return f"Error getting tasks: {str(error)}"
  • src/main.py:85-85 (registration)
    Registers the todoist_get_tasks handler as an MCP tool using FastMCP's decorator.
    mcp.tool()(todoist_get_tasks)
  • src/main.py:26-36 (registration)
    Imports the todoist_get_tasks function from tasks module for registration.
    from .tasks import (
        todoist_get_task,
        todoist_get_tasks,
        todoist_filter_tasks,
        todoist_add_task,
        todoist_update_task,
        todoist_complete_task,
        todoist_uncomplete_task,
        todoist_move_task,
        todoist_delete_task,
    )
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions that the tool 'Get[s] a list of tasks' and supports filters, but lacks details on permissions, rate limits, pagination, or response format. For a read operation with zero annotation coverage, this is insufficient to inform the agent about key behavioral traits like safety or constraints.

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

Conciseness4/5

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

The description is appropriately sized and front-loaded: the first sentence states the purpose, followed by a structured 'Args:' section. Each parameter explanation is brief and relevant, with no wasted sentences. Minor improvements could include merging the purpose and filter details more seamlessly, but overall it's efficient.

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 the tool's moderate complexity (4 optional parameters, no output schema, no annotations), the description is partially complete. It covers parameter semantics well but lacks behavioral context, usage guidelines, and output details. For a read tool with filtering, it's adequate as a minimum viable description but has clear gaps in guiding the agent fully.

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

Parameters4/5

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

Schema description coverage is 0%, so the description must compensate. It adds meaningful context for all four parameters: 'project_id' filters by project ID, 'filter' uses natural language examples, 'priority' specifies levels 1-4, and 'limit' sets maximum returns. This goes beyond the schema's basic titles, providing practical usage hints, though it could include more details like format constraints.

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 tool's purpose: 'Get a list of tasks from Todoist with various filters.' It specifies the verb ('Get') and resource ('tasks from Todoist'), making the action clear. However, it doesn't explicitly differentiate from sibling tools like 'todoist_get_task' (singular) or mention that this is for batch retrieval versus single-task operations, which prevents a perfect score.

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. It doesn't mention siblings like 'todoist_get_task' for single tasks or 'todoist_get_projects' for project lists, nor does it specify prerequisites or contexts for filtering. Usage is implied through parameter descriptions but not explicitly stated, leaving gaps for agent decision-making.

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

Related 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/mikemc/todoist-mcp-server'

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