Skip to main content
Glama

update_task

Modify existing tasks in LunaTask by updating fields like name, status, priority, or notes to keep productivity data current.

Instructions

Update an existing task in LunaTask.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idYes
nameNo
noteNo
area_idNo
statusNo
priorityNo
scheduled_onNo
motivationNo
eisenhowerNo
estimateNo
progressNo
goal_idNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The core handler function that executes the logic to update a task via the LunaTask API.
    async def update_task_tool(  # noqa: PLR0913, PLR0911, PLR0915, PLR0912, C901
        lunatask_client: LunaTaskClient,
        ctx: Context,
        id: str,  # noqa: A002
        name: str | None = None,
        note: str | None = None,
        area_id: str | None = None,
        status: str | None = None,
        priority: int | str | None = None,
        scheduled_on: str | None = None,
        motivation: str | None = None,
        eisenhower: int | str | None = None,
        estimate: int | str | None = None,
        progress: int | str | None = None,
        goal_id: str | None = None,
    ) -> dict[str, Any]:
        """Update an existing task in LunaTask.
    
        This MCP tool updates an existing task using the LunaTask API. The task ID
        is required, and any combination of other fields can be updated.
    
        Args:
            ctx: MCP context for logging and communication
            id: Task ID to update (required)
            name: Updated task name (optional)
            note: Updated task note (optional)
            area_id: Updated area ID the task belongs to (optional)
            status: Updated task status (optional)
            priority: Updated task priority level (optional)
            scheduled_on: Updated scheduled date in YYYY-MM-DD format (optional)
            motivation: Updated task motivation (must, should, want, unknown) (optional)
            eisenhower: Updated eisenhower matrix quadrant (0-4) (optional)
            estimate: Updated estimated duration in minutes (optional)
            progress: Updated task completion percentage (optional)
            goal_id: Updated goal ID the task belongs to (optional)
    
        Returns:
            dict[str, Any]: Response containing task update result with updated task data
    
        Raises:
            LunaTaskValidationError: When task validation fails (422)
            LunaTaskNotFoundError: When task is not found (404)
            LunaTaskAuthenticationError: When authentication fails (401)
            LunaTaskRateLimitError: When rate limit exceeded (429)
            LunaTaskServerError: When server error occurs (5xx)
            LunaTaskAPIError: For other API errors
        """
        # Validate required task ID parameter
        if not id or not id.strip():
            error_msg = "Task ID cannot be empty"
            result = {
                "success": False,
                "error": "validation_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("Empty task_id provided for update")
            return result
    
        # Validate that at least one field is provided for update
        update_fields = [
            name,
            note,
            area_id,
            status,
            priority,
            scheduled_on,
            motivation,
            eisenhower,
            estimate,
            progress,
            goal_id,
        ]
        if all(field is None for field in update_fields):
            error_msg = "At least one field must be provided for update"
            result = {
                "success": False,
                "error": "validation_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("No fields provided for task update: %s", id)
            return result
    
        # Parse and validate scheduled_on if provided
        parsed_scheduled_on = None
        if scheduled_on is not None:
            try:
                parsed_scheduled_on = date.fromisoformat(scheduled_on)
            except (ValueError, TypeError) as e:
                error_msg = f"Invalid scheduled_on format. Expected YYYY-MM-DD format: {e}"
                result = {
                    "success": False,
                    "error": "validation_error",
                    "message": error_msg,
                }
                await ctx.error(error_msg)
                logger.warning("Invalid scheduled_on format for task %s: %s", id, scheduled_on)
                return result
    
        # Coerce string priority values to integers when possible for client UX
        coerced_priority: int | None = None
        if priority is not None:
            if isinstance(priority, int):
                coerced_priority = priority
            else:
                try:
                    coerced_priority = int(priority)
                except (TypeError, ValueError):
                    error_msg = "Invalid priority: must be an integer between -2 and 2"
                    result = {
                        "success": False,
                        "error": "validation_error",
                        "message": f"Validation failed for priority: {error_msg}",
                    }
                    await ctx.error(error_msg)
                    logger.warning("Invalid priority type for task %s: %r", id, priority)
                    return result
    
        # Coerce string eisenhower values to integers when possible for client UX
        coerced_eisenhower: int | None = None
        if eisenhower is not None:
            if isinstance(eisenhower, int):
                coerced_eisenhower = eisenhower
            else:
                try:
                    coerced_eisenhower = int(eisenhower)
                except (TypeError, ValueError):
                    error_msg = "Invalid eisenhower: must be an integer between 0 and 4"
                    result = {
                        "success": False,
                        "error": "validation_error",
                        "message": f"Validation failed for eisenhower: {error_msg}",
                    }
                    await ctx.error(error_msg)
                    logger.warning("Invalid eisenhower type for task %s: %r", id, eisenhower)
                    return result
    
        # Coerce string estimate values to integers when possible for client UX
        coerced_estimate: int | None = None
        if estimate is not None:
            if isinstance(estimate, int):
                coerced_estimate = estimate
            else:
                try:
                    coerced_estimate = int(estimate)
                except (TypeError, ValueError):
                    error_msg = "Invalid estimate: must be an integer (minutes)"
                    result = {
                        "success": False,
                        "error": "validation_error",
                        "message": f"Validation failed for estimate: {error_msg}",
                    }
                    await ctx.error(error_msg)
                    logger.warning("Invalid estimate type for task %s: %r", id, estimate)
                    return result
    
        # Coerce string progress values to integers when possible for client UX
        coerced_progress: int | None = None
        if progress is not None:
            if isinstance(progress, int):
                coerced_progress = progress
            else:
                try:
                    coerced_progress = int(progress)
                except (TypeError, ValueError):
                    error_msg = "Invalid progress: must be an integer (percentage)"
                    result = {
                        "success": False,
                        "error": "validation_error",
                        "message": f"Validation failed for progress: {error_msg}",
                    }
                    await ctx.error(error_msg)
                    logger.warning("Invalid progress type for task %s: %r", id, progress)
                    return result
    
        await ctx.info(f"Updating task {id}")
    
        try:
            # Create TaskUpdate object from provided parameters
            # Cast string parameters to proper Literal types for optional fields
            task_status = None
            if status is not None:
                task_status = (
                    status if status in ("later", "next", "started", "waiting", "completed") else None
                )
    
            task_motivation = None
            if motivation is not None:
                task_motivation = (
                    motivation if motivation in ("must", "should", "want", "unknown") else None
                )
    
            # Build kwargs to avoid passing None (preserve model defaults and PATCH semantics)
            update_kwargs: dict[str, Any] = {"id": id}
            if area_id is not None:
                update_kwargs["area_id"] = area_id
            if name is not None:
                update_kwargs["name"] = name
            if note is not None:
                update_kwargs["note"] = note
            if task_status is not None:
                update_kwargs["status"] = task_status
            if coerced_priority is not None:
                update_kwargs["priority"] = coerced_priority
            if parsed_scheduled_on is not None:
                update_kwargs["scheduled_on"] = parsed_scheduled_on
            if task_motivation is not None:
                update_kwargs["motivation"] = task_motivation
            if coerced_eisenhower is not None:
                update_kwargs["eisenhower"] = coerced_eisenhower
            if coerced_estimate is not None:
                update_kwargs["estimate"] = coerced_estimate
            if coerced_progress is not None:
                update_kwargs["progress"] = coerced_progress
            if goal_id is not None:
                update_kwargs["goal_id"] = goal_id
    
            task_update = TaskUpdate(**update_kwargs)
    
            # Use LunaTask client to update the task
            async with lunatask_client as client:
                updated_task = await client.update_task(id, task_update)
    
            # Return success response with updated task data
            result = {
                "success": True,
                "task_id": id,
                "message": "Task updated successfully",
                "task": serialize_task_response(updated_task),
            }
    
        except LunaTaskNotFoundError as e:
            # Handle task not found errors (404)
            error_msg = f"Task not found: {e}"
            result = {
                "success": False,
                "error": "not_found_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("Task not found during update: %s", id)
            return result
    
        except LunaTaskValidationError as e:
            # Handle validation errors (422)
            error_msg = f"Task validation failed: {e}"
            result = {
                "success": False,
                "error": "validation_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("Task validation error during update: %s", e)
            return result
    
        except LunaTaskAuthenticationError as e:
            # Handle authentication errors (401)
            error_msg = f"Authentication failed: {e}"
            result = {
                "success": False,
                "error": "authentication_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("Authentication error during task update: %s", e)
            return result
    
        except LunaTaskRateLimitError as e:
            # Handle rate limit errors (429)
            error_msg = f"Rate limit exceeded: {e}"
            result = {
                "success": False,
                "error": "rate_limit_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("Rate limit exceeded during task update: %s", e)
            return result
    
        except LunaTaskServerError as e:
            # Handle server errors (5xx)
            error_msg = f"Server error: {e}"
            result = {
                "success": False,
                "error": "server_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("Server error during task update: %s", e)
            return result
    
        except LunaTaskAPIError as e:
            # Handle other API errors
            error_msg = f"API error: {e}"
            result = {
                "success": False,
                "error": "api_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.warning("API error during task update: %s", e)
            return result
    
        except Exception as e:
            # Handle Pydantic validation errors specifically
            if "ValidationError" in str(type(e)) and hasattr(e, "errors"):
                # Handle Pydantic validation errors with structured MCP response
                error_details: list[str] = []
                for error in e.errors():  # type: ignore[attr-defined]
                    field = error.get("loc", ["unknown"])[0] if error.get("loc") else "unknown"  # type: ignore[misc]
                    msg = error.get("msg", "Invalid value")  # type: ignore[misc]
                    if field == "motivation":
                        msg = "Must be one of: must, should, want, unknown"
                    elif field == "eisenhower":
                        msg = "Must be between 0 and 4"
                    elif field == "priority":
                        msg = "Must be between -2 and 2"
                    elif field == "status":
                        msg = "Must be one of: later, next, started, waiting, completed"
                    elif field == "estimate":
                        msg = "Must be a positive integer (minutes)"
                    elif field == "progress":
                        msg = "Must be an integer between 0 and 100 (percentage)"
                    error_details.append(f"{field}: {msg}")
    
                error_msg = f"Validation failed for {', '.join(error_details)}"
                result = {
                    "success": False,
                    "error": "validation_error",
                    "message": error_msg,
                }
                await ctx.error(error_msg)
                logger.warning("Task validation error during update: %s", error_msg)
                return result
            # Handle unexpected errors
            error_msg = f"Unexpected error updating task: {e}"
            result = {
                "success": False,
                "error": "unexpected_error",
                "message": error_msg,
            }
            await ctx.error(error_msg)
            logger.exception("Unexpected error during task update")
            return result
        else:
            await ctx.info(f"Successfully updated task {id}")
            return result
  • Registration of the "update_task" tool with the FastMCP instance within the TaskTools class.
    self.mcp.tool("update_task")(_update_task_tool)
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 states this is an update operation but doesn't mention permissions needed, whether partial updates are allowed, what happens to unspecified fields, error conditions, or response format. For a mutation tool with 12 parameters, this leaves significant gaps.

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 gets straight to the point with zero wasted words. It's appropriately sized for a basic tool description, though it could benefit from additional context.

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 complexity (12 parameters, mutation operation), lack of annotations, and 0% schema coverage, the description is inadequate. While an output schema exists (which helps with return values), the description doesn't address critical aspects like parameter meanings, update behavior, error handling, or usage context that would help an agent invoke it correctly.

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

Parameters1/5

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

Schema description coverage is 0%, meaning none of the 12 parameters have descriptions in the schema. The tool description provides no information about any parameters—not even the required 'id' parameter or what the various fields like 'eisenhower', 'area_id', or 'progress' mean. This fails to compensate for the complete lack of schema 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 action ('Update') and resource ('an existing task in LunaTask'), making the purpose immediately understandable. It doesn't distinguish from sibling tools like 'update_note' or 'create_task', but it's not misleading or tautological.

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?

No guidance is provided on when to use this tool versus alternatives like 'create_task' or 'delete_task'. The description doesn't mention prerequisites, constraints, or typical scenarios for updating tasks versus creating new ones.

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/tensorfreitas/lunatask-mcp'

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