# 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.10.3) | 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` | | Listen | `create_list`, `update_list`, `delete_list` | | Kategorien | `create_category`, `delete_category` | | Aktivitäten | `like_post` | | Rezepte | `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe`, `get_recipe_categories` | | 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) ✓ ← 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/_` → `family/` ### 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. 🌱