feat(tasks): implement clear_due_date via FiZ \$empty sentinel (v0.4.16)

The Family Wall API (FiZ framework) uses the special string "\$empty"
to clear optional date fields. Sending dueDate=\$empty to taskupdate2
reliably removes the due date (verified via direct API probing).

- update_task(clear_due_date=True) now sends dueDate=\$empty instead
  of returning an error
- Remove the "not supported" limitation message from the docstring
- Update SPEC.md, README.md, CLAUDE.md to document the discovery
- Bump version to 0.4.16

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 10:57:21 +02:00
parent 6c955d67c8
commit 0517241ee5
6 changed files with 39 additions and 37 deletions
+2 -2
View File
@@ -23,7 +23,7 @@ eingebunden.
## Aktueller Stand ## Aktueller Stand
### Implementierte Tools (v0.4.15) ### Implementierte Tools (v0.4.16)
| Kategorie | Tools | | Kategorie | Tools |
|---|---| |---|---|
@@ -31,7 +31,7 @@ eingebunden.
| Listen | `get_lists` | | Listen | `get_lists` |
| Tasks (Lesen) | `get_tasks` (inkl. `category_id`, `due_date`, `assignee_ids`), `get_categories` (inkl. `custom`-Flag) | | Tasks (Lesen) | `get_tasks` (inkl. `category_id`, `due_date`, `assignee_ids`), `get_categories` (inkl. `custom`-Flag) |
| Wall | `get_activities`, `like_post` | | 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) | | Kategorien (Schreiben) | `create_category` (inkl. `icon`), `delete_category` (System-Kategorien geschützt) |
+2 -2
View File
@@ -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. 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 ### Read
@@ -16,7 +16,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your
### Write (with confirmation prompt) ### Write (with confirmation prompt)
- `create_task` -- create a new task in a list (supports `category_id`, `due_date`, `assignee_ids`) - `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 - `toggle_task` -- mark a task complete or reopen it
- `delete_task` -- permanently delete a task - `delete_task` -- permanently delete a task
- `create_category` -- create a custom category for a shopping list (with optional icon) - `create_category` -- create a custom category for a shopping list (with optional icon)
+23 -18
View File
@@ -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. **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 | | Wert | API-Response |
|---|---| |---|---|
| `""` (leerer String) | `a00.un.un: " is not a valid Date"` | | `""` (leerer String) | `a00.un: " is not a valid Date"` |
| `"null"` | `a00.un.un: "Cannot parse date val=null"` | | `"null"` | `a00.un: "Cannot parse date val=null"` |
| `"0"` | API-Fehler (ungültiges Datum) | | `"undefined"` | `a00.un: "Cannot parse date val=undefined"` |
| `"-1"` | `a00.un.un: "-1 is not a valid Date"` | | `"0"`, `"-1"` | `a00.un: "... is not a valid Date"` |
| `"remove"` / `"clear"` | `a00.un.un: "Cannot parse date val=..."` | | `"remove"` / `"clear"` | `a00.un: "Cannot parse date val=..."` |
| `"0000-00-00T00:00:00"` | API-Fehler | | `"1970-01-01T00:00:00"` | Erfolgreich setzt dueDate auf Unix-Epoch (kein Clearing!) |
| `"1970-01-01T00:00:00"` | **Erfolgreich** setzt dueDate auf Unix-Epoch (kein echtes Clearing!) | | Feld weglassen | dueDate bleibt unverändert |
| Separate Parameter (`removeDueDate`, `clearDueDate`, `clearFields`) | werden ignoriert, dueDate bleibt unverändert | | `removeDueDate=1`, `clearDueDate=1` | werden ignoriert |
| Feld weglassen (kein `dueDate` in Request) | dueDate bleibt unverändert |
**Wichtig:** Die Fehler werden als `a00.un.un` zurückgegeben (nicht Top-Level `un`). **Wichtig:** Die Fehler werden als `a00.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: Ab v0.4.15 erkennt `fw_client` diese und wirft `FamilyWallError`.
`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.
### `taskmark` Task als erledigt/offen markieren ### `taskmark` Task als erledigt/offen markieren
POST https://api.familywall.com/api/taskmark 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) - ~~`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`: Task verschieben~~ → **`taskListId`** als optionaler Parameter setzt neue Liste (verifiziert)
- `taskupdate2`: Alle Zuweisungen entfernen (leere `assignee`-Liste) → noch nicht 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) - ~~`taskupdate2`: `dueDate` entfernen (Clearing)~~**`dueDate=$empty`** (FiZ-Sentinel, verifiziert April 2026, v0.4.16)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "mcp-familywall" 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" description = "MCP server for Family Wall — read your family's lists and tasks via Claude"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.15" __version__ = "0.4.16"
+10 -13
View File
@@ -758,10 +758,8 @@ def update_task(
shopping lists; ignored for TODO lists. shopping lists; ignored for TODO lists.
due_date: New due date in ISO 8601 format (e.g. ``"2026-04-30T18:00:00"``). 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*. 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. clear_due_date: Set to ``True`` to remove the due date from the task.
**Note:** The Family Wall API does not support clearing a due date once Cannot be used together with *due_date*.
set — this will return an informational error. This parameter exists to
surface the limitation explicitly rather than silently doing nothing.
assignee_ids: New list of member IDs from get_members (e.g. ``["23431898"]``). 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. 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 list_id: Move the task to a different list by providing the target list ID
@@ -771,23 +769,19 @@ def update_task(
Returns: Returns:
JSON success indicator or an error message. JSON success indicator or an error message.
""" """
if clear_due_date: if clear_due_date and due_date is not None:
return ( return "Error: 'clear_due_date' and 'due_date' cannot be used together."
"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 ( if (
text is None text is None
and description is None and description is None
and category_id is None and category_id is None
and due_date is None and due_date is None
and not clear_due_date
and assignee_ids is None and assignee_ids is None
and list_id 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} params: dict[str, Any] = {"metaId": task_id}
if text is not None: if text is not None:
@@ -796,7 +790,10 @@ def update_task(
params["description"] = description params["description"] = description
if category_id is not None: if category_id is not None:
params["taskCategoryId"] = category_id 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 params["dueDate"] = due_date
if assignee_ids is not None: if assignee_ids is not None:
params["assignee"] = assignee_ids if assignee_ids else "" params["assignee"] = assignee_ids if assignee_ids else ""