diff --git a/CLAUDE.md b/CLAUDE.md index eb25c72..bded01b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,7 +23,7 @@ eingebunden. ## Aktueller Stand -### Implementierte Tools (v0.4.15) +### Implementierte Tools (v0.4.16) | Kategorie | Tools | |---|---| @@ -31,7 +31,7 @@ eingebunden. | Listen | `get_lists` | | Tasks (Lesen) | `get_tasks` (inkl. `category_id`, `due_date`, `assignee_ids`), `get_categories` (inkl. `custom`-Flag) | | Wall | `get_activities`, `like_post` | -| Tasks (Schreiben) | `create_task` (inkl. `category_id`, `due_date`, `assignee_ids`), `update_task` (inkl. `category_id`, `due_date`, `assignee_ids`, `list_id`), `toggle_task`, `delete_task` | +| Tasks (Schreiben) | `create_task` (inkl. `category_id`, `due_date`, `assignee_ids`), `update_task` (inkl. `category_id`, `due_date`, `clear_due_date`, `assignee_ids`, `list_id`), `toggle_task`, `delete_task` | | Kategorien (Schreiben) | `create_category` (inkl. `icon`), `delete_category` (System-Kategorien geschützt) | diff --git a/README.md b/README.md index df39deb..28b24d1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your family's circles, lists, and tasks directly from Claude. -## Features (v0.4.15) +## Features (v0.4.16) ### Read @@ -16,7 +16,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your ### Write (with confirmation prompt) - `create_task` -- create a new task in a list (supports `category_id`, `due_date`, `assignee_ids`) -- `update_task` -- update text, description, category, due date, assignees, or move to a different list (note: clearing a due date is not supported by the Family Wall API) +- `update_task` -- update text, description, category, due date, assignees, or move to a different list; supports `clear_due_date=True` to remove a due date - `toggle_task` -- mark a task complete or reopen it - `delete_task` -- permanently delete a task - `create_category` -- create a custom category for a shopping list (with optional icon) diff --git a/SPEC.md b/SPEC.md index 8a88e0d..87bfffd 100644 --- a/SPEC.md +++ b/SPEC.md @@ -315,28 +315,33 @@ Hinweis: `taskListId` ist optional – ohne diesen Parameter bleibt der Task in **Response-Struktur:** kein spezifischer Rückgabewert – Erfolg = kein `ex`/`un`-Key auf Top-Level. -**dueDate Clearing – nicht implementierbar (Stand: April 2026):** +**dueDate Clearing – `"$empty"` Sentinel (verifiziert April 2026, v0.4.16):** -Ausgiebig getestete Werte für `dueDate`, die alle fehlschlugen: +Der FiZ-Framework verwendet den Sentinel-Wert `"$empty"` um optionale Felder zu leeren. + +``` +POST taskupdate2 +metaId=task/23431854_726333919&dueDate=$empty +→ dueDate wird auf null gesetzt ✓ +``` + +`update_task(clear_due_date=True)` sendet intern `dueDate=$empty`. + +**Getestete Werte die fehlschlugen (vor der Lösung):** | Wert | API-Response | |---|---| -| `""` (leerer String) | `a00.un.un: " is not a valid Date"` | -| `"null"` | `a00.un.un: "Cannot parse date val=null"` | -| `"0"` | API-Fehler (ungültiges Datum) | -| `"-1"` | `a00.un.un: "-1 is not a valid Date"` | -| `"remove"` / `"clear"` | `a00.un.un: "Cannot parse date val=..."` | -| `"0000-00-00T00:00:00"` | API-Fehler | -| `"1970-01-01T00:00:00"` | **Erfolgreich** – setzt dueDate auf Unix-Epoch (kein echtes Clearing!) | -| Separate Parameter (`removeDueDate`, `clearDueDate`, `clearFields`) | werden ignoriert, dueDate bleibt unverändert | -| Feld weglassen (kein `dueDate` in Request) | dueDate bleibt unverändert | +| `""` (leerer String) | `a00.un: " is not a valid Date"` | +| `"null"` | `a00.un: "Cannot parse date val=null"` | +| `"undefined"` | `a00.un: "Cannot parse date val=undefined"` | +| `"0"`, `"-1"` | `a00.un: "... is not a valid Date"` | +| `"remove"` / `"clear"` | `a00.un: "Cannot parse date val=..."` | +| `"1970-01-01T00:00:00"` | Erfolgreich – setzt dueDate auf Unix-Epoch (kein Clearing!) | +| Feld weglassen | dueDate bleibt unverändert | +| `removeDueDate=1`, `clearDueDate=1` | werden ignoriert | -**Wichtig:** Die Fehler werden als `a00.un.un` zurückgegeben (nicht Top-Level `un`). -Der `fw_client` prüfte ursprünglich nur Top-Level `un` → silent fail (ab v0.4.15 behoben: -`a00.un` wird ebenfalls erkannt und als `FamilyWallError` geworfen). - -Das `dueDate`-Feld kann einmal gesetzt nicht mehr entfernt werden. -`update_task(clear_due_date=True)` gibt eine klare Fehlermeldung zurück. +**Wichtig:** Die Fehler werden als `a00.un` zurückgegeben (nicht Top-Level `un`). +Ab v0.4.15 erkennt `fw_client` diese und wirft `FamilyWallError`. ### `taskmark` – Task als erledigt/offen markieren POST https://api.familywall.com/api/taskmark @@ -543,4 +548,4 @@ AND `moodStarShortcut: false` AND `moodMap: {}`. - ~~`taskcreate2` / `taskupdate2`: Zuweisung-Parameter~~ → **`assignee`** (Member-ID String; mehrere Werte = mehrfach senden); gespeichert als `assigneeIds[]` (verifiziert) - ~~`taskupdate2`: Task verschieben~~ → **`taskListId`** als optionaler Parameter setzt neue Liste (verifiziert) - `taskupdate2`: Alle Zuweisungen entfernen (leere `assignee`-Liste) → noch nicht verifiziert -- ~~`taskupdate2`: `dueDate` entfernen (Clearing)~~ → **nicht möglich** (verifiziert, April 2026) — alle getesteten Werte werden als ungültiges Datum abgelehnt (siehe unten) \ No newline at end of file +- ~~`taskupdate2`: `dueDate` entfernen (Clearing)~~ → **`dueDate=$empty`** (FiZ-Sentinel, verifiziert April 2026, v0.4.16) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index bceea9f..12003bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mcp-familywall" -version = "0.4.15" +version = "0.4.16" description = "MCP server for Family Wall — read your family's lists and tasks via Claude" readme = "README.md" requires-python = ">=3.12" diff --git a/src/mcp_familywall/__init__.py b/src/mcp_familywall/__init__.py index 5a4bb1d..6ff6db1 100644 --- a/src/mcp_familywall/__init__.py +++ b/src/mcp_familywall/__init__.py @@ -1 +1 @@ -__version__ = "0.4.15" +__version__ = "0.4.16" diff --git a/src/mcp_familywall/server.py b/src/mcp_familywall/server.py index b05e68d..ee19f05 100644 --- a/src/mcp_familywall/server.py +++ b/src/mcp_familywall/server.py @@ -758,10 +758,8 @@ def update_task( shopping lists; ignored for TODO lists. due_date: New due date in ISO 8601 format (e.g. ``"2026-04-30T18:00:00"``). Omit to leave unchanged. Cannot be used together with *clear_due_date*. - clear_due_date: Set to ``True`` to request removal of the due date. - **Note:** The Family Wall API does not support clearing a due date once - set — this will return an informational error. This parameter exists to - surface the limitation explicitly rather than silently doing nothing. + clear_due_date: Set to ``True`` to remove the due date from the task. + Cannot be used together with *due_date*. assignee_ids: New list of member IDs from get_members (e.g. ``["23431898"]``). Pass an empty list to remove all assignees. Omit to leave unchanged. list_id: Move the task to a different list by providing the target list ID @@ -771,23 +769,19 @@ def update_task( Returns: JSON success indicator or an error message. """ - if clear_due_date: - return ( - "Error: Removing a due date is not supported by the Family Wall API. " - "The 'dueDate' field cannot be cleared once set — this is a known " - "limitation of the API (all tested values are rejected as invalid dates). " - "You can set a different future date instead." - ) + if clear_due_date and due_date is not None: + return "Error: 'clear_due_date' and 'due_date' cannot be used together." if ( text is None and description is None and category_id is None and due_date is None + and not clear_due_date and assignee_ids is None and list_id is None ): - return "Error: At least one of 'text', 'description', 'category_id', 'due_date', 'assignee_ids', or 'list_id' must be provided." + return "Error: At least one of 'text', 'description', 'category_id', 'due_date', 'clear_due_date', 'assignee_ids', or 'list_id' must be provided." params: dict[str, Any] = {"metaId": task_id} if text is not None: @@ -796,7 +790,10 @@ def update_task( params["description"] = description if category_id is not None: params["taskCategoryId"] = category_id - if due_date is not None: + if clear_due_date: + # The FiZ framework uses "$empty" as a sentinel to clear optional date fields. + params["dueDate"] = "$empty" + elif due_date is not None: params["dueDate"] = due_date if assignee_ids is not None: params["assignee"] = assignee_ids if assignee_ids else ""