Files
mcp-familywall/CLAUDE.md
T
marcus 0b56ea92bc docs(recipes): document isRecipe-flag behavior in get_recipe_box (v0.11.8)
Documentation-only release explaining the isRecipe flag:
- isRecipe='true': Real recipes in the recipe box
- isRecipe='false': Free-text stubs from meal planner OR old imported recipes
  never properly tagged (e.g. recipe/16282169_7055369 'Elsässer Flammkuchen')

get_recipe_box filters strictly on isRecipe='true' — consistent with Family Wall app.

Updated SPEC.md, README.md, CLAUDE.md with v0.11.8.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-17 14:09:28 +02:00

205 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# mcp-familywall
## 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
### Implementierte Tools (v0.11.8)
| Kategorie | Tools |
|---|---|
| Lesen | `get_circles`, `get_members`, `get_lists`, `get_tasks`, `get_categories`, `get_activities`, `get_meal_plan` |
| Tasks | `create_task`, `update_task`, `toggle_task`, `delete_task`, `clear_list` |
| Listen | `create_list`, `update_list`, `delete_list` |
| Kategorien | `create_category`, `delete_category` |
| Aktivitäten | `like_post` |
| Rezepte | `get_recipes`, `get_recipe_box`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe`, `get_recipe_categories` |
| Essensplaner | `get_meal_plan`, `add_recipe_to_meal_plan`, `add_meal_to_meal_plan`, `add_meal_note`, `delete_meal_plan_entry` |
| Kreise | `create_circle`, `update_circle`, `delete_circle`, `add_member_to_circle` |
## Roadmap
- v0.4.x: Kategorie-Management, Task-Felder (due_date, assignee, list_id) ✓
- v0.5.x: Listen-Management (create_list, delete_list) ✓
- v0.5.1: emoji + color in get_lists / create_list ✓
- v0.5.2: Mengenkonvention im create_task Docstring ✓
- v0.5.3: Kategorie-Auto-Assign-Hinweis im create_task Docstring ✓ (nachgeliefert in v0.6.1)
- v0.6.0: Rezept-Box (get_recipes, get_recipe, create_recipe, delete_recipe) ✓
- v0.6.1: update_recipe + Bugfix Zeilenumbrüche in create_recipe ✓
- v0.7.0: create_circle + add_member_to_circle ✓
- v0.7.1: get_lists scope fix + create_list circle_id + delete_list scope ✓
- v0.7.2: delete_circle ✓
- v0.7.3: update_list (Umbenennen, emoji/color ändern) ✓
- v0.7.4: update_circle (Kreis umbenennen) ✓
- v0.7.5: Primärkreis-Schutz in update_circle (isFirstFamily-Check) ✓
- v0.8.0: Rezept-Kategorien (get_recipe_categories, create_recipe + category_ids, update_recipe + category_ids) ✓
- v0.8.1: Bugfixes (recipe categories) ✓
- v0.8.2: get_lists() ohne scope → alle Kreise ✓
- v0.8.3: OTHER-Listentyp dokumentiert + create_list unterstützt ihn; FW_DEBUG=1 loggt unbekannte Task-Felder (Vorbereitung Wiederholungen) ✓
- v0.8.x: mpadditemtolist (gestrichen Family Wall kann das nativ)
- v0.9.0: get_tasks liefert recurrency, recurrency_interval, rrule, reminder (read-only) ✓
- v0.9.1: Bugfix reminder-Mapping (reminderUnit/reminderValue statt unit/value; value=0 ist gültig) ✓
- v0.10.0: get_meal_plan (read-only, raw JSON; Premium-Feature Essensplaner) ✓
- v0.10.1: get_meal_plan strukturierter Output + SPEC.md mplistinterval Response verifiziert ✓
- v0.10.2: get_meal_plan mealList[] einbinden (Freitext-Notizen + Portionen), merged + sortiert ✓
- v0.10.3: get_meal_plan is_from_recipe_box Feld (recipeList[].isRecipe Lookup) ✓
- v0.11.0: add_recipe_to_meal_plan (mpcreateByRecipeId; raw response bis Struktur verifiziert) ✓
- v0.11.1: add_recipe_to_meal_plan strukturierter Output (Response verifiziert) ✓
- v0.11.2: add_meal_to_meal_plan (mpcreate; Freitext; raw response bis Struktur verifiziert) ✓
- v0.11.3: add_meal_to_meal_plan strukturierter Output (a00.r.r ist Array, nicht Objekt) ✓
- v0.11.4: delete_meal_plan_entry (metadelete für dish/ und meal/-Objekte) ✓
- v0.11.5: add_meal_note (mpmealput; Notiz + Portionen; strukturierter Output) ✓
- v0.11.6: clear_list (alle Tasks einer Liste in einer Session löschen; bulk delete) ✓
- v0.11.7: get_recipe_box (nur echte Rezepte, isRecipe=true) + Parser-Fix ingredients_parsed aus Freitext statt API-List ✓
- v0.11.8: get_recipe_box isRecipe-Flag-Dokumentation (Freitext-Stubs vs. echte Rezepte) ✓ ← aktuell
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
## 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` |
| `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. 🌱