From 572cc4dd3c81d06232984614f4487ac14c8e69dd Mon Sep 17 00:00:00 2001 From: Isaac Johnson Date: Tue, 14 Oct 2025 07:16:53 -0500 Subject: [PATCH] 0.21 - fix label update with details. updating lables was not working --- Dockerfile | 2 +- gemini-extension.json | 4 +-- main.py | 57 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index e056a1b..9f7797b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,4 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python", "main.py"] -#harbor.freshbrewed.science/library/vikunjamcp:0.20 +#harbor.freshbrewed.science/library/vikunjamcp:0.21 diff --git a/gemini-extension.json b/gemini-extension.json index a492978..72be395 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,6 +1,6 @@ { "name": "vikunja", - "version": "1.0.20", + "version": "1.0.21", "mcpServers": { "nodeServer": { "command": "docker", @@ -14,7 +14,7 @@ "VIKUNJA_USERNAME", "-e", "VIKUNJA_PASSWORD", - "harbor.freshbrewed.science/library/vikunjamcp:0.20" + "harbor.freshbrewed.science/library/vikunjamcp:0.21" ], "env": { "VIKUNJA_URL": "$VIKUNJA_URL", diff --git a/main.py b/main.py index 5cda223..64b6d4e 100644 --- a/main.py +++ b/main.py @@ -579,14 +579,43 @@ def update_task_details( set_if(is_favorite, "is_favorite") set_if(hex_color, "hex_color") - # Replace labels if provided as comma-separated string + # Prepare labels payload separately because Vikunja's task update endpoint + # does not accept label updates; labels are managed via the labels bulk API. + labels_payload = None if labels is not None: + # normalize into array of strings (label titles) or ids if isinstance(labels, str): parsed = [s.strip() for s in labels.split(",") if s.strip()] - payload["labels"] = parsed + labels_payload = {"labels": parsed} + elif isinstance(labels, list): + # If list contains dicts with id/title, pass through; otherwise coerce + normalized = [] + for item in labels: + if isinstance(item, dict): + # prefer 'title' or 'name' or 'id' + if 'title' in item: + normalized.append(item['title']) + elif 'name' in item: + normalized.append(item['name']) + elif 'id' in item: + normalized.append(item['id']) + else: + normalized.append(str(item)) + else: + normalized.append(item) + labels_payload = {"labels": normalized} + elif isinstance(labels, dict): + # single dict -> try to extract title or id + if 'title' in labels: + labels_payload = {"labels": [labels['title']]} + elif 'name' in labels: + labels_payload = {"labels": [labels['name']]} + elif 'id' in labels: + labels_payload = {"labels": [labels['id']]} + else: + labels_payload = {"labels": [str(labels)]} else: - # Allow callers to pass list directly - payload["labels"] = labels + labels_payload = {"labels": [str(labels)]} if not payload: return "No fields provided to update." @@ -595,7 +624,25 @@ def update_task_details( logger.info("update_task_details: updating task_id=%s with payload keys=%s", task_id, list(payload.keys())) response = session.post(f"{VIKUNJA_URL}/api/v1/tasks/{task_id}", json=payload) response.raise_for_status() - return response.json() + task_update_resp = response.json() + + # If labels were provided, send them to the labels bulk endpoint + if labels_payload is not None: + try: + logger.info("update_task_details: updating labels for task_id=%s labels=%s", task_id, labels_payload) + lab_resp = session.post(f"{VIKUNJA_URL}/api/v1/tasks/{task_id}/labels/bulk", json=labels_payload) + lab_resp.raise_for_status() + logger.info("update_task_details: labels updated successfully for task_id=%s", task_id) + except requests.exceptions.RequestException as le: + resp = getattr(le, 'response', None) + if resp is not None: + try: + logger.error("update_task_details: labels update returned status=%s body=%s", resp.status_code, resp.text) + except Exception: + logger.exception("update_task_details: failed to read labels response body for task_id=%s", task_id) + logger.exception("update_task_details: labels update request failed for task_id=%s", task_id) + + return task_update_resp except requests.exceptions.RequestException as e: logger.exception("update_task_details: request failed for task_id=%s", task_id) return f"Error updating task details: {e}"