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
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| name | No | ||
| note | No | ||
| area_id | No | ||
| status | No | ||
| priority | No | ||
| scheduled_on | No | ||
| motivation | No | ||
| eisenhower | No | ||
| estimate | No | ||
| progress | No | ||
| goal_id | No |
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 - src/lunatask_mcp/tools/tasks.py:303-303 (registration)Registration of the "update_task" tool with the FastMCP instance within the TaskTools class.
self.mcp.tool("update_task")(_update_task_tool)