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

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)

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