5671d70000
Add explicit guidance to resolve raw API IDs to human-readable names before user display. Affected tools: get_tasks, get_lists, get_activities, get_wall_posts, get_members, get_circles, get_categories, get_meal_plan. Add general usability rule to CLAUDE.md implementation section: never present raw numeric/metaIDs to the user. No breaking changes; docstring improvements only. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
190 lines
9.9 KiB
Markdown
190 lines
9.9 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.4.3** ← aktuell
|
||
|
||
### Implementierte Tools
|
||
|
||
| Kategorie | Tools |
|
||
|---|---|
|
||
| Wall & Aktivitäten | `get_wall_posts`, `create_wall_post`, `add_comment`, `like_post` |
|
||
| 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` |
|
||
| 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: Weitere Wall-Post Features (Edits, Deletes, Emoji-Reactions beyond STAR)
|
||
|
||
### 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)
|
||
- v1.1: Task-Recurrency-Write (flat top-level params)
|
||
- v1.2: Task-Reminder-Write via Dot-Notation (`reminder.*`) — verifiziert 2026-04-17
|
||
|
||
|
||
## 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
|
||
|
||
**Usability rule:** Never present raw API IDs (numeric IDs, metaIds, category IDs, circle IDs, member IDs) to the user.
|
||
Always resolve them to human-readable names before responding.
|
||
|
||
- **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 (Dot-Notation!) | `reminder.reminderUnit` (`MINUTE`/`HOUR`/`DAY`), `reminder.reminderValue` (String-Integer), `reminder.reminderType` (`SNOOZE`=aktiv, `NONE`=entfernen), `reminder.localId` optional. Entfernen: vollständigen Block mit `reminderType=NONE, reminderValue="0", reminderUnit=MINUTE` senden. Partielle Updates → `task reminder invalid`. |
|
||
| `taskupdate2` | **⚠️ Encoding** | Recurrency flach top-level. Reminder **nur Dot-Notation** `reminder.*` — flache Keys, JSON-String, Brackets werden silent-ignored. |
|
||
| `taskmark` | `taskId`, `complete` | `"true"`/`"false"` |
|
||
| `metadelete` | `id` | metaId des Tasks / Rezepts |
|
||
| `wallmood` | `wall_message_id`, `add`, `remove`/`remove.0` | Like: `add="STAR", remove="$empty"`; Unlike: `add="$empty", remove.0="STAR"` (Array-Dot-Notation) |
|
||
| `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
|
||
Unlike via `remove.0=STAR` (Array-Dot-Notation). Verifiziert 2026-04-17 via Network-Interceptor.
|
||
|
||
### 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. 🌱
|