d6d8d40305
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>
185 lines
9.3 KiB
Markdown
185 lines
9.3 KiB
Markdown
# mcp-familywall
|
||
|
||
**Note:** This file is intentionally written in German (developer preference).
|
||
Code, docstrings, and commit messages are in English.
|
||
|
||
## Kontext
|
||
|
||
Dieses Projekt entwickelt `mcp-familywall` – einen MCP-Server für den
|
||
Zugriff auf Family Wall (familywall.com). Der MCP-Server läuft lokal
|
||
und wird in Claude Desktop eingebunden.
|
||
|
||
|
||
## Infrastruktur
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Family Wall API** | `https://api.familywall.com/api` |
|
||
| **Gitea** | `https://gitea.gecheckt.de/marcus/mcp-familywall` |
|
||
| **Lokaler Code** | `D:\Dev\Projects\mcp-familywall` |
|
||
| **Sprache** | Python 3.12+, `uv`, MCP SDK, `httpx`, `keyring`, `click`, `rich` |
|
||
|
||
|
||
## Deploy-Workflow (nach jeder Code-Änderung)
|
||
|
||
1. Claude Code committet und pusht (bei Berechtigungsfehler: bis zu 2 Retries, je 1s warten)
|
||
2. Marcus installiert lokal und startet Claude Desktop neu
|
||
|
||
## Aktueller Stand
|
||
|
||
### Version: **v1.1.2** ← aktuell
|
||
|
||
### Implementierte Tools
|
||
|
||
| Kategorie | Tools |
|
||
|---|---|
|
||
| Kreise & Mitglieder | `get_circles`, `get_members`, `create_circle`, `update_circle`, `delete_circle`, `add_member_to_circle` |
|
||
| Listen & Tasks | `get_lists`, `get_tasks`, `get_categories`, `get_activities`, `create_list`, `update_list`, `delete_list`, `create_category`, `delete_category`, `create_task`, `update_task`, `toggle_task`, `delete_task`, `clear_list`, `like_post` |
|
||
| Rezeptbox | `get_recipe_categories`, `get_recipe_box`, `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe` |
|
||
| Essensplaner | `get_meal_plan`, `add_recipe_to_meal_plan`, `add_meal_to_meal_plan`, `add_meal_note`, `delete_meal_plan_entry` |
|
||
|
||
### Roadmap (Nächstes)
|
||
|
||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
||
|
||
### Historische Meilensteine (kompakt)
|
||
|
||
- v0.1–v0.4: Lesen (Kreise, Listen, Tasks, Kategorien), Schreiben (Tasks, Kategorien)
|
||
- v0.5: Listen-Management (create/delete), emoji + color
|
||
- v0.6–v0.8: Rezeptbox vollständig, Kreismanagement, Rezeptkategorien
|
||
- v0.9: Task-Wiederholungen + Erinnerungen (read-only)
|
||
- v0.10–v0.11: Essensplaner (read + write)
|
||
- v1.0: Cleanup, Unified errors, Datumsvalidierung, Partial-Failure-Reporting (Details: CHANGELOG.md)
|
||
|
||
|
||
## Architektur-Entscheidungen
|
||
|
||
### Session-Strategie
|
||
Kein Session-Caching. Jeder Tool-Call führt Login → API-Call → Logout durch.
|
||
Optimierung: Mehrere API-Calls in einer Session bündeln (login → call1 → call2 → logout)
|
||
um unnötige HTTP-Roundtrips zu vermeiden. Credentials liegen im OS Keyring
|
||
(nur `email` + `password`), kein `session_id`.
|
||
|
||
### Kreise (Scopes)
|
||
Family Wall kennt mehrere Kreise (z.B. Familie, erweiterter Familienkreis).
|
||
Der API-Parameter `scope=family/XXXX` schaltet den Server-Kontext um.
|
||
- `taskgettasklists` ohne scope → primärer Kreis; mit scope → angegebener Kreis
|
||
- `taskcreatelist` mit scope → neue Liste im angegebenen Kreis
|
||
- `taskdeletelist` mit scope → löscht Liste aus angegebenem Kreis
|
||
- `get_lists(scope="family/XXXX")` oder `get_lists(scope="Kreis-Name")` zur Filterung
|
||
- Die Listen-metaId kodiert den Kreis: `taskList/<FAMNUM>_<LISTNUM>` → `family/<FAMNUM>`
|
||
|
||
### Listen-Namen
|
||
Systembezeichnungen (z.B. `SYS-CAT-SHOPPINGLIST`) werden in deutsche
|
||
Klarnamen übersetzt. Mapping-Tabelle in `modules/lists.py`.
|
||
|
||
### Kategorien
|
||
- Kategorien sind family-wide, nicht list-spezifisch (`taskcategoryput` gilt für alle Listen)
|
||
- System-Kategorien: `rights.canDelete=null` → nicht löschbar
|
||
- Custom-Kategorien: `rights.canDelete="true"` → löschbar
|
||
- Locale-Filter: default `"de"` – Custom-Kategorien haben kein locale-Feld
|
||
und werden immer angezeigt unabhängig vom Locale-Parameter
|
||
|
||
### Fehlerbehandlung
|
||
Die API gibt Fehler manchmal als `a00.un.un` zurück (nicht Top-Level).
|
||
`fw_client.py` prüft beides und wirft `FamilyWallError`. Nie silent-fail
|
||
als Erfolg werten – immer `a00.un` und Top-Level `ex`/`un` prüfen.
|
||
|
||
### Service Worker
|
||
Die Family Wall Web-App nutzt einen Service Worker der bestimmte
|
||
HTTP-Requests abfängt und modifiziert. Browser-DevTools und JS-Interceptoren
|
||
können den echten Request-Body in diesen Fällen nicht sehen.
|
||
→ Immer `FW_DEBUG=1` für Traffic-Analyse nutzen, nicht Browser-DevTools.
|
||
|
||
|
||
## Claude Code – Implementierungsregeln
|
||
|
||
- **Feature complete before next feature** – jedes Feature vollständig
|
||
implementieren, testen und verifizieren bevor das nächste beginnt
|
||
- **Kein destruktives Probing** – keine Probe-Calls auf System-Kategorien,
|
||
echte Listen oder echte Tasks; immer Test-Objekte anlegen und danach
|
||
sofort löschen
|
||
- Fehlerbehandlung: API-Fehler als verständliche Meldung zurückgeben, keine Stacktraces
|
||
- Keine Secrets in stderr-Ausgaben (Passwort bei Debug-Logging maskieren)
|
||
- Type Hints und Docstrings konsequent verwenden (Englisch)
|
||
- Formatter: `ruff format`, Linter: `ruff check`, Tests: `pytest`
|
||
- Alle Texte (Docstrings, Kommentare, README): Englisch
|
||
- Debug-Logging via `FW_DEBUG=1` Umgebungsvariable
|
||
- Nach jeder Aufgabe: git commit + push. Bei Berechtigungsfehler: 1s warten, bis zu 2 Retries
|
||
- .gitignore eigenständig pflegen (Credentials, __pycache__, .venv, .env, *.pyc etc.)
|
||
- README.md + CLAUDE.md im Projekt-Root bei jedem Aufruf aktualisieren
|
||
- Confirmation-Pattern in Docstrings: `IMPORTANT: Ask the user for confirmation
|
||
before calling this tool.` für destruktive oder schreibende Operationen
|
||
|
||
|
||
## Bekannte API-Eigenheiten und Fallstricke
|
||
|
||
### Sentinel-Wert `$empty`
|
||
Das FiZ-Framework nutzt `$empty` als Sentinel um optionale Felder zu löschen.
|
||
Normale Werte wie `""`, `null`, `"null"`, `"0"` werden vom Server abgelehnt.
|
||
Aktuell genutzt für: `dueDate=$empty` (Fälligkeitsdatum entfernen)
|
||
|
||
### Silent-Fail via `a00.un.un`
|
||
Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
|
||
```json
|
||
{"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
|
||
```
|
||
→ `fw_client` muss beide Ebenen prüfen
|
||
|
||
### Bekannte Parameter-Namen (verifiziert)
|
||
| Endpoint | Parameter | Wert/Format |
|
||
|---|---|---|
|
||
| `taskcreate2` | `taskListId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee` | – |
|
||
| `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 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 |
|
||
| `taskcategoryput` | `name`, `emoji` | – |
|
||
| `taskcategorydelete` | `id` | metaId der Kategorie |
|
||
| `taskcreatelist` | `name`, `taskListType`, `sharedToAll`, `color`, `emoji`, `scope` | `taskListType`: `SHOPPING_LIST`, `TODOS`, `OTHER`; `scope`: Kreis-metaId für nicht-primäre Kreise |
|
||
| `taskgettasklists` | `scope` | Kreis-metaId; ohne scope → primärer Kreis |
|
||
| `taskupdatelist` | `metaId`, `name`, `color`, `emoji`, `scope` | `metaId` ⚠️ nicht `id`!; `scope`: Kreis-metaId für sekundäre Kreise; Partial Update |
|
||
| `taskdeletelist` | `id`, `scope` | `scope`: Kreis-metaId für sekundäre Kreise |
|
||
| `mprecipeput` | `recipe.name`, `recipe.isRecipe="true"`, `recipe.description`, `recipe.ingredients`, `recipe.instructions`, `recipe.prepTime`, `recipe.cookTime`, `recipe.serves`, `recipe.url` | Alle mit `recipe.`-Prefix! |
|
||
| `mprecipeput` (Update) | zusätzlich `recipe.metaId` | Vorhandene ID → Update statt Create |
|
||
| `mprecipeput` (Kategorien) | `recipe.recipeCategoryIdList` | Mehrfach sendbar; leerer String `""` entfernt alle Kategorien |
|
||
| `metasync` (Rezepte lesen) | `id="recipe"` | liefert `a00.r.r.updatedCreated[]` |
|
||
| `acccreatefamily` | `name` | liefert numerische Kreis-ID als String in `a00.r.r` |
|
||
| `accinvite` | `familyId`, `identifier`, `role="Unknown"`, `firstname` | nur für neue FW-Accounts |
|
||
| `accupdatefamily` | `name`, `scope` | `scope`: Kreis-metaId; ohne scope → PRIMARY Kreis; erster Buchstabe wird kapitalisiert |
|
||
| `adminwipefamily` | `scope` | Kreis-metaId; löscht Kreis + alle Inhalte; `a00.r.r="true"` bei Erfolg |
|
||
|
||
### Self-Like-Restriction
|
||
Eigene Posts können nicht geliked werden. API antwortet 200, macht aber nichts.
|
||
|
||
### Unlike nicht möglich
|
||
Service Worker verschlüsselt den Unlike-Request-Body.
|
||
Endpoint unbekannt. `like_post(like=False)` gibt Fehlermeldung zurück.
|
||
|
||
### mpstar / Rezept-Favorit
|
||
Service Worker fängt `mpstar` ab. `metamood` funktioniert nur auf
|
||
fremde Inhalte (Self-Reaction-Restriction). Eigene Rezepte
|
||
können nicht als Favorit markiert werden. Siehe SPEC.md § Offene Punkte.
|
||
|
||
|
||
## Test-Credentials (nur für Entwicklung)
|
||
|
||
| | |
|
||
|---|---|
|
||
| E-Mail | `marcus@gecheckt.de` |
|
||
| Passwort | `Lasdas1234` |
|
||
|
||
Hinweis: Das ist ein kostenloser Test-Account ohne Premium-Features.
|
||
Der echte Account (Premium) hat andere Credentials die im Keyring gespeichert sind.
|
||
|
||
|
||
## Hintergrund
|
||
|
||
Marcus ist Senior Software Engineer (Java, Jakarta EE).
|
||
Präferenz: State-of-the-Art, Best Practices, saubere Architektur.
|
||
Automatisierung spart Zeit für die Familie. 🌱
|