fix(tasks): remove non-functional reminder write params (v1.1.2)
Exhaustive FW_DEBUG investigation on both Free and Premium accounts confirmed that taskupdate2 silently ignores all reminder fields regardless of encoding (flat, JSON-string, PHP-bracket, all variants). Alternative endpoints (tasksetalert etc.) are unregistered. Root cause: mobile app Service Worker transforms reminder requests in ways not reproducible via direct API calls. Reminders remain read-only. Recurrency write confirmed working (flat encoding). Docs updated with full investigation findings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
This project follows Semantic Versioning (SemVer).
|
||||
Breaking changes (removed tools, changed parameters) increment the major version.
|
||||
|
||||
## [1.1.2] – 2026-04-17
|
||||
|
||||
### Fixed
|
||||
- `update_task`: removed non-functional reminder write parameters (`reminder_unit`,
|
||||
`reminder_value`, `clear_reminder`) — exhaustive FW_DEBUG testing (flat params,
|
||||
JSON-string, PHP-bracket, all string values, with/without `localId`, alternative
|
||||
endpoints) on both Free and Premium accounts confirmed that `taskupdate2` silently
|
||||
ignores all reminder fields; historical SNOOZE reminders were set via the mobile
|
||||
app's Service Worker which transforms requests in ways not reproducible via direct API
|
||||
- `update_task`: docstring now explicitly states reminders are read-only
|
||||
- SPEC.md: documented reminder write limitation with investigation findings
|
||||
- CLAUDE.md: updated reminder row to reflect read-only status
|
||||
|
||||
### Investigation notes
|
||||
- Tested formats: flat int params, flat string params, JSON-encoded string value,
|
||||
PHP-bracket notation (`reminder[reminderUnit]=DAY`), no-`localId` variant
|
||||
- Tested endpoints: `tasksetalert`, `taskalertput`, `taskreminderset`, `taskalert`,
|
||||
`tasknotification` — all return `"The call X is not registered"`
|
||||
- Verified on both test account (Free) and real account (Premium) — same result
|
||||
- Recurrency write via flat params is confirmed working (separate from reminder)
|
||||
|
||||
---
|
||||
|
||||
## [1.1.1] – 2026-04-17
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -27,7 +27,7 @@ und wird in Claude Desktop eingebunden.
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
### Version: **v1.1.0** ← aktuell
|
||||
### Version: **v1.1.2** ← aktuell
|
||||
|
||||
### Implementierte Tools
|
||||
|
||||
@@ -133,8 +133,8 @@ Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
|
||||
| `taskupdate2` | `metaId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee`, `taskListId` | – |
|
||||
| `taskupdate2` | `dueDate` löschen | `$empty` |
|
||||
| `taskupdate2` | `recurrencyDescriptor` (flach!) | `recurrency, recurrencyInterval, rrule, byDay, byMonthDay, recurrencyEndDate, endOccurence` als Top-Level-Parameter; löschen: `recurrency="NONE"` |
|
||||
| `taskupdate2` | `reminder` (flach!) | `reminderUnit: "MINUTE"\|"HOUR"\|"DAY", reminderValue: int, reminderType: "SNOOZE", localId: 0` als Top-Level-Parameter; löschen: `reminderUnit=""` |
|
||||
| `taskupdate2` | **⚠️ Encoding** | FiZ `Ai()`-Encoder sendet alle Felder flach — KEINE verschachtelten Objekte für recurrencyDescriptor/reminder! |
|
||||
| `taskupdate2` | **⚠️ Reminder read-only** | `reminderUnit`, `reminderValue`, `reminderType`, `localId` werden von der API ignoriert (verifiziert via FW_DEBUG auf Premium-Account). Service Worker der mobilen App transformiert Reminder-Requests — nicht reproduzierbar via direkter API. |
|
||||
| `taskupdate2` | **⚠️ Encoding** | FiZ `Ai()`-Encoder sendet Recurrency-Felder flach (verifiziert). Reminder: read-only. |
|
||||
| `taskmark` | `taskId`, `complete` | `"true"`/`"false"` |
|
||||
| `metadelete` | `id` | metaId des Tasks / Rezepts |
|
||||
| `wallmood` | `wall_message_id`, `moodType` | `"STAR"` für Like |
|
||||
|
||||
@@ -208,16 +208,20 @@ POST https://api.familywall.com/api/taskupdate2
|
||||
| `byMonthDay` | nein | int (z.B. `7` für "am 7. des Monats") |
|
||||
| `recurrencyEndDate` | nein | ISO-Datum (z.B. `"2026-12-31"`) |
|
||||
| `endOccurence` | nein | int (nach N Wiederholungen aufhören) |
|
||||
| `reminderUnit` | nein | `"MINUTE"` \| `"HOUR"` \| `"DAY"` (löschen: `""`) |
|
||||
| `reminderValue` | nein | int (`0` = zum Zeitpunkt, `15` = 15 min vorher); löschen: `0` |
|
||||
| `reminderType` | nein | immer `"SNOOZE"` |
|
||||
| `localId` | nein | immer `0` |
|
||||
|
||||
**Hinweis:** `taskListId` ist NICHT Pflicht beim Update.
|
||||
|
||||
**⚠️ Encoding:** Der FiZ-`Ai()`-Encoder serialisiert alle Felder **flach** als Top-Level-Form-Parameter.
|
||||
`recurrencyDescriptor` und `reminder` sind **keine** verschachtelten JSON-Objekte — die Felder
|
||||
werden direkt auf Top-Level gesendet (verifiziert via xb-Encoder / fc-Encoder im JS-Bundle).
|
||||
Recurrency-Felder werden direkt auf Top-Level gesendet (verifiziert via xb-Encoder im JS-Bundle).
|
||||
|
||||
**⚠️ Reminder-Schreibzugriff nicht möglich:**
|
||||
Alle Versuche, `reminderUnit`, `reminderValue`, `reminderType`, `localId` via `taskupdate2`
|
||||
zu setzen, wurden ignoriert — die API antwortet mit HTTP 200 und `lastAction: UPDATED`,
|
||||
aber `reminder` bleibt unverändert auf dem Default-Wert `{reminderUnit: MINUTE, reminderType: NONE, reminderValue: 0}`.
|
||||
Auch alternative Encodings (JSON-String, PHP-Bracket, verschachtelt) und alternative Endpoints
|
||||
(`tasksetalert`, `taskalertput` etc.) wurden erprobt — alle Endpoints sind nicht registriert.
|
||||
**Ursache:** Reminder-Updates werden vom Service Worker der mobilen App abgefangen und transformiert.
|
||||
Diese Transformation ist nicht reproduzierbar ohne den Service Worker.
|
||||
Reminder sind daher **read-only** (wie in v0.9 dokumentiert).
|
||||
|
||||
**Response:**
|
||||
```
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mcp-familywall"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
description = "MCP server for Family Wall — manage your family's circles, lists, tasks, recipes, and meal plan via Claude"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -930,15 +930,17 @@ def update_task(
|
||||
recurrency_interval: int | None = None,
|
||||
rrule: str | None = None,
|
||||
clear_recurrency: bool = False,
|
||||
reminder_unit: str | None = None,
|
||||
reminder_value: int | None = None,
|
||||
clear_reminder: bool = False,
|
||||
) -> str:
|
||||
"""Update an existing task's fields.
|
||||
|
||||
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||
At least one parameter besides *task_id* must be provided.
|
||||
|
||||
NOTE: Setting or clearing reminders is **not supported** via this tool.
|
||||
The Family Wall API does not accept reminder writes through ``taskupdate2``
|
||||
— reminder updates are handled exclusively by the mobile app's Service
|
||||
Worker and cannot be replicated via direct API calls.
|
||||
|
||||
Args:
|
||||
task_id: Task metaId from get_tasks.
|
||||
text: New title text (omit to leave unchanged).
|
||||
@@ -967,14 +969,6 @@ def update_task(
|
||||
Overrides simple recurrency fields when set.
|
||||
clear_recurrency: Set to ``True`` to remove the recurrence rule from the task.
|
||||
Cannot be used together with *recurrency*.
|
||||
reminder_unit: Unit for the reminder offset — ``"MINUTE"``, ``"HOUR"``, or
|
||||
``"DAY"``. Must be provided together with *reminder_value*. Requires a
|
||||
*due_date* to be set on the task.
|
||||
reminder_value: Offset before the due date/time (e.g. ``0`` = at the time,
|
||||
``15`` = 15 min before, ``1`` with unit ``"DAY"`` = 1 day before).
|
||||
Must be provided together with *reminder_unit*.
|
||||
clear_reminder: Set to ``True`` to remove the reminder from the task.
|
||||
Cannot be used together with *reminder_unit*/*reminder_value*.
|
||||
|
||||
Returns:
|
||||
JSON success indicator or an error message.
|
||||
@@ -983,12 +977,6 @@ def update_task(
|
||||
return _err("'clear_due_date' and 'due_date' cannot be used together.")
|
||||
if clear_recurrency and recurrency is not None:
|
||||
return _err("'clear_recurrency' and 'recurrency' cannot be used together.")
|
||||
if clear_reminder and (reminder_unit is not None or reminder_value is not None):
|
||||
return _err(
|
||||
"'clear_reminder' cannot be used together with 'reminder_unit'/'reminder_value'."
|
||||
)
|
||||
if (reminder_unit is None) != (reminder_value is None):
|
||||
return _err("'reminder_unit' and 'reminder_value' must be provided together.")
|
||||
|
||||
if (
|
||||
text is None
|
||||
@@ -1000,14 +988,11 @@ def update_task(
|
||||
and list_id is None
|
||||
and recurrency is None
|
||||
and not clear_recurrency
|
||||
and reminder_unit is None
|
||||
and not clear_reminder
|
||||
):
|
||||
return _err(
|
||||
"At least one of 'text', 'description', 'category_id', 'due_date',"
|
||||
" 'clear_due_date', 'assignee_ids', 'list_id', 'recurrency',"
|
||||
" 'clear_recurrency', 'reminder_unit'/'reminder_value', or"
|
||||
" 'clear_reminder' must be provided."
|
||||
" 'clear_due_date', 'assignee_ids', 'list_id', 'recurrency', or"
|
||||
" 'clear_recurrency' must be provided."
|
||||
)
|
||||
|
||||
params: dict[str, Any] = {"metaId": task_id}
|
||||
@@ -1027,9 +1012,7 @@ def update_task(
|
||||
if list_id is not None:
|
||||
params["taskListId"] = list_id
|
||||
|
||||
# The FiZ Ai() encoder serialises all fields flat at the top level of the
|
||||
# request body — nested objects are NOT supported. recurrencyDescriptor and
|
||||
# reminder fields must therefore be sent as plain top-level parameters.
|
||||
# Recurrency fields are sent flat at the top level (verified: FiZ Ai() encoder).
|
||||
if clear_recurrency:
|
||||
params["recurrency"] = "NONE"
|
||||
elif recurrency is not None:
|
||||
@@ -1039,17 +1022,6 @@ def update_task(
|
||||
if rrule is not None:
|
||||
params["rrule"] = rrule
|
||||
|
||||
if clear_reminder:
|
||||
params["reminderUnit"] = ""
|
||||
params["reminderValue"] = 0
|
||||
params["reminderType"] = "SNOOZE"
|
||||
params["localId"] = 0
|
||||
elif reminder_unit is not None and reminder_value is not None:
|
||||
params["reminderUnit"] = reminder_unit
|
||||
params["reminderValue"] = reminder_value
|
||||
params["reminderType"] = "SNOOZE"
|
||||
params["localId"] = 0
|
||||
|
||||
try:
|
||||
_authenticated_call("taskupdate2", params)
|
||||
except RuntimeError as exc:
|
||||
|
||||
Reference in New Issue
Block a user