From 4cd0bdc4991d044e27d7ff0b2a538ddc1816899f Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Wed, 15 Apr 2026 16:12:05 +0200 Subject: [PATCH] fix: use moodStarShortcut+moodMap for liked state, warn on silent fail (v0.4.6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FW_DEBUG=1 investigation revealed: - wallmood can silently fail (200 OK, frozen modifDate, no state change) due to self-like restriction, unsupported post type (FAMILY_CREATED), or rate limit - Response contains two complementary like indicators: moodStarShortcut (primary) and moodMap (secondary) — both must be checked - liked: false with like=True now surfaces a warning instead of silently returning a misleading result - SPEC.md documents silent-fail scenarios and dual indicator pattern Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 +- SPEC.md | 21 +++++++++++++++++++-- pyproject.toml | 2 +- src/mcp_familywall/server.py | 27 ++++++++++++++++++++------- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 82ea6db..ec16e87 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.5) +## Features (v0.4.6) ### Read diff --git a/SPEC.md b/SPEC.md index c7ea6b8..cfb01ac 100644 --- a/SPEC.md +++ b/SPEC.md @@ -324,8 +324,25 @@ a00.r.r → vollständiges Wall-Message-Objekt .rights.canDelete → "true"/"false" ``` -**Like-Zustand bestimmen:** `moodMap[accountId]` enthält `["STAR"]` wenn der -jeweilige Account geliked hat. Leere Map oder fehlender Key = kein Like. +**Like-Zustand bestimmen (zwei Indikatoren, beide auswerten):** + +| Feld | Typ | Bedeutung | +|---|---|---| +| `moodStarShortcut` | `"true"` / `"false"` | Primär: direktes User-Like-Flag für den anfragenden Account | +| `moodMap[accountId]` | `["STAR"]` | Sekundär: accountId → Mood-Liste; enthält `"STAR"` wenn geliked | + +Beide Indikatoren können den Like-Zustand korrekt abbilden — je nach API-internem +Speicherpfad ist nur einer gesetzt. Immer beide prüfen. + +**Silent-Fail-Szenarien (API antwortet 200, aber Like wird nicht gesetzt):** +- **Self-Like-Restriction**: Eigener Post kann nicht geliked werden + (verifiziert: Account 23431898 kann Post `wall/23431854_31119189` nicht liken, + obwohl API regulär antwortet — `modifDate` bleibt eingefroren) +- **Unsupported Post-Typ**: `FAMILY_CREATED`-Posts ignorieren wallmood-Calls +- **Rate-Limit**: Nach vielen Calls kann die API Still-Fails zurückgeben + +Erkennungsmerkmal für Silent-Fail: `modifDate` im Response identisch zum Vorherigen +AND `moodStarShortcut: false` AND `moodMap: {}`. ## Noch zu verifizieren diff --git a/pyproject.toml b/pyproject.toml index d3a3e24..0346725 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mcp-familywall" -version = "0.4.5" +version = "0.4.6" 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/server.py b/src/mcp_familywall/server.py index 333dbfc..5ff2e16 100644 --- a/src/mcp_familywall/server.py +++ b/src/mcp_familywall/server.py @@ -532,14 +532,27 @@ def like_post(post_id: str, like: bool = True) -> str: indent=2, ) - # Confirm the STAR is present in moodMap (server-side representation of a like). - now_liked = any("STAR" in moods for moods in mood_map.values()) + # Two complementary indicators for the like state: + # - moodStarShortcut: direct boolean per-user flag on the post object (primary) + # - moodMap: dict of accountId → [mood types]; contains "STAR" when liked (secondary) + # Use both so either storage path is covered. + star_shortcut = wall_obj.get("moodStarShortcut") == "true" + star_in_map = any("STAR" in moods for moods in mood_map.values()) + now_liked = star_shortcut or star_in_map - return json.dumps( - {"liked": now_liked, "id": post_id, "author": account_id}, - ensure_ascii=False, - indent=2, - ) + result: dict[str, Any] = {"liked": now_liked, "id": post_id, "author": account_id} + + # Surface a warning when the like call apparently had no effect, so the + # caller can distinguish a successful like from a silent API rejection + # (e.g. rate limit, unsupported post type, or self-like restriction). + if not now_liked: + result["warning"] = ( + "Like may not have been applied. " + "Possible causes: rate limit, unsupported post type (e.g. FAMILY_CREATED), " + "or self-like restriction." + ) + + return json.dumps(result, ensure_ascii=False, indent=2) # ---------------------------------------------------------------------------