feat(meal-planner): add add_meal_note tool (v0.11.5)
New write tool using mpmealput endpoint to create meal/ note entries with optional free-text and serving count. Response structure verified from JS-bundle (Sg class); a00.r.r is a plain object (unlike mpcreate). Structured output matches get_meal_plan meal entry format. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ und wird in Claude Desktop eingebunden.
|
|||||||
|
|
||||||
## Aktueller Stand
|
## Aktueller Stand
|
||||||
|
|
||||||
### Implementierte Tools (v0.11.4)
|
### Implementierte Tools (v0.11.5)
|
||||||
|
|
||||||
| Kategorie | Tools |
|
| Kategorie | Tools |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -34,7 +34,7 @@ und wird in Claude Desktop eingebunden.
|
|||||||
| Kategorien | `create_category`, `delete_category` |
|
| Kategorien | `create_category`, `delete_category` |
|
||||||
| Aktivitäten | `like_post` |
|
| Aktivitäten | `like_post` |
|
||||||
| Rezepte | `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe`, `get_recipe_categories` |
|
| Rezepte | `get_recipes`, `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`, `delete_meal_plan_entry` |
|
| 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` |
|
| Kreise | `create_circle`, `update_circle`, `delete_circle`, `add_member_to_circle` |
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
@@ -67,7 +67,8 @@ und wird in Claude Desktop eingebunden.
|
|||||||
- v0.11.1: add_recipe_to_meal_plan strukturierter Output (Response 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.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.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) ✓ ← aktuell
|
- 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) ✓ ← aktuell
|
||||||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
MCP server for [Family Wall](https://www.familywall.com) -- read and manage your family's circles, lists, tasks, and recipes directly from Claude.
|
MCP server for [Family Wall](https://www.familywall.com) -- read and manage your family's circles, lists, tasks, and recipes directly from Claude.
|
||||||
|
|
||||||
## Features (v0.11.4)
|
## Features (v0.11.5)
|
||||||
|
|
||||||
### Read
|
### Read
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your
|
|||||||
- `delete_recipe` -- permanently delete a recipe (only own recipes)
|
- `delete_recipe` -- permanently delete a recipe (only own recipes)
|
||||||
- `add_recipe_to_meal_plan` -- add a recipe from the recipe box to the meal plan for a specific date and meal slot (BREAKFAST/LUNCH/SNACK/DINNER)
|
- `add_recipe_to_meal_plan` -- add a recipe from the recipe box to the meal plan for a specific date and meal slot (BREAKFAST/LUNCH/SNACK/DINNER)
|
||||||
- `add_meal_to_meal_plan` -- add a free-text meal entry (no recipe) to the meal plan for a specific date and meal slot
|
- `add_meal_to_meal_plan` -- add a free-text meal entry (no recipe) to the meal plan for a specific date and meal slot
|
||||||
|
- `add_meal_note` -- add a note and/or serving count to a meal plan slot (creates a `meal/` entry; at least one of `note` or `serves` required)
|
||||||
- `delete_meal_plan_entry` -- permanently delete a meal plan entry (works for both `dish/…` and `meal/…` entries)
|
- `delete_meal_plan_entry` -- permanently delete a meal plan entry (works for both `dish/…` and `meal/…` entries)
|
||||||
- `create_circle` -- create a new Family Wall circle (group)
|
- `create_circle` -- create a new Family Wall circle (group)
|
||||||
- `update_circle` -- rename a circle (server capitalises first letter; only name is changeable via API; primary circle is protected)
|
- `update_circle` -- rename a circle (server capitalises first letter; only name is changeable via API; primary circle is protected)
|
||||||
|
|||||||
@@ -788,11 +788,42 @@ verknüpft es mit dem Dish-Objekt. `is_from_recipe_box` ist daher `false`.
|
|||||||
|
|
||||||
**Verifiziert am:** 2026-04-17 via FW_DEBUG=1
|
**Verifiziert am:** 2026-04-17 via FW_DEBUG=1
|
||||||
|
|
||||||
|
### `mpmealput` – Meal-Notiz erstellen/aktualisieren
|
||||||
|
POST https://api.familywall.com/api/mpmealput
|
||||||
|
|
||||||
|
**Body-Parameter (verifiziert aus JS-Bundle, Encoder `UI(O)`):**
|
||||||
|
|
||||||
|
| Parameter | Pflicht | Wert |
|
||||||
|
|---|---|---|
|
||||||
|
| `date` | ja | Ziel-Datum ISO 8601 (z.B. `"2026-04-20"`) |
|
||||||
|
| `type` | ja | Mahlzeiten-Typ: `BREAKFAST`, `LUNCH`, `SNACK`, `DINNER` |
|
||||||
|
| `note` | nein | Freitext-Notiz (z.B. `"Bitte ohne Zwiebeln"`) |
|
||||||
|
| `serves` | nein | Portionen als String (z.B. `"4"`) — Vt = int→string Konverter |
|
||||||
|
| `metaId` | nein | metaId eines bestehenden meal/-Objekts → Update; ohne → Create |
|
||||||
|
|
||||||
|
**Hinweis:** Ohne `metaId` wird ein neues `meal/`-Objekt erstellt.
|
||||||
|
Mit `metaId` wird ein bestehendes aktualisiert (Update, noch nicht implementiert).
|
||||||
|
|
||||||
|
**Response-Struktur:**
|
||||||
|
```
|
||||||
|
a00.r.r → meal-Objekt (Objekt, nicht Array)
|
||||||
|
.metaId → neue/aktualisierte Meal-ID (z.B. "meal/16282169_...")
|
||||||
|
.date → Datum
|
||||||
|
.type → Mahlzeiten-Typ
|
||||||
|
.note → Freitext-Notiz
|
||||||
|
.serves → Portionen als String (z.B. "1")
|
||||||
|
.familyId → Kreis-metaId
|
||||||
|
.accountId → Ersteller-accountId
|
||||||
|
.rights.canUpdate → "true"
|
||||||
|
.rights.canDelete → "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verifiziert am:** 2026-04-17 (Parameter aus JS-Bundle; Response-Struktur aus `Sg`-Klasse im Bundle)
|
||||||
|
|
||||||
### Weitere Meal Planner Endpoints (nicht implementiert)
|
### Weitere Meal Planner Endpoints (nicht implementiert)
|
||||||
|
|
||||||
| Endpoint | Parameter | Bedeutung |
|
| Endpoint | Parameter | Bedeutung |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `mpmealput` | Mahlzeiten-Objekt (encoded) | Mahlzeit aktualisieren |
|
|
||||||
| `mpmove` | `metaId`, `date`, `type`, `clientOpId` | Mahlzeit zu anderem Datum/Typ verschieben |
|
| `mpmove` | `metaId`, `date`, `type`, `clientOpId` | Mahlzeit zu anderem Datum/Typ verschieben |
|
||||||
| `mpdelete` | `metaId` | Mahlzeit löschen |
|
| `mpdelete` | `metaId` | Mahlzeit löschen |
|
||||||
| `mpsettings` | – | Einstellungen lesen |
|
| `mpsettings` | – | Einstellungen lesen |
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "mcp-familywall"
|
name = "mcp-familywall"
|
||||||
version = "0.11.4"
|
version = "0.11.5"
|
||||||
description = "MCP server for Family Wall — read your family's lists and tasks via Claude"
|
description = "MCP server for Family Wall — read your family's lists and tasks via Claude"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.11.4"
|
__version__ = "0.11.5"
|
||||||
|
|||||||
@@ -2499,6 +2499,79 @@ def delete_meal_plan_entry(entry_id: str) -> str:
|
|||||||
return json.dumps({"deleted": True, "id": entry_id}, ensure_ascii=False, indent=2)
|
return json.dumps({"deleted": True, "id": entry_id}, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tool: add_meal_note
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def add_meal_note(
|
||||||
|
date: str,
|
||||||
|
meal_type: str,
|
||||||
|
note: str | None = None,
|
||||||
|
serves: int | None = None,
|
||||||
|
) -> str:
|
||||||
|
"""Add a note and/or serving count to a meal plan slot.
|
||||||
|
|
||||||
|
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||||
|
|
||||||
|
Creates a ``meal/`` entry for the given date and meal type.
|
||||||
|
At least one of ``note`` or ``serves`` must be provided.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
date: Target date in ISO format (e.g. ``"2026-04-20"``).
|
||||||
|
meal_type: Meal slot — one of ``"BREAKFAST"``, ``"LUNCH"``,
|
||||||
|
``"SNACK"``, or ``"DINNER"``.
|
||||||
|
note: Optional free-text note (e.g. ``"Bitte ohne Zwiebeln"``).
|
||||||
|
serves: Optional number of servings as integer (e.g. ``4``).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON with the new meal entry on success, or an error message.
|
||||||
|
"""
|
||||||
|
if meal_type not in ("BREAKFAST", "LUNCH", "SNACK", "DINNER"):
|
||||||
|
return "Error: meal_type must be one of 'BREAKFAST', 'LUNCH', 'SNACK', 'DINNER'."
|
||||||
|
if note is None and serves is None:
|
||||||
|
return "Error: At least one of 'note' or 'serves' must be provided."
|
||||||
|
|
||||||
|
params: dict[str, Any] = {"date": date, "type": meal_type}
|
||||||
|
if note is not None:
|
||||||
|
params["note"] = note
|
||||||
|
if serves is not None:
|
||||||
|
params["serves"] = str(serves) # API expects string (int→string)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = _authenticated_call("mpmealput", params)
|
||||||
|
except RuntimeError as exc:
|
||||||
|
return f"Error: {exc}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
meal = data["a00"]["r"]["r"]
|
||||||
|
if not isinstance(meal, dict) or "metaId" not in meal:
|
||||||
|
raise TypeError("unexpected shape")
|
||||||
|
except (KeyError, TypeError):
|
||||||
|
return json.dumps(
|
||||||
|
{"warning": "Unexpected mpmealput response structure", "raw": data},
|
||||||
|
ensure_ascii=False,
|
||||||
|
indent=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
rights = meal.get("rights") or {}
|
||||||
|
raw_serves = meal.get("serves")
|
||||||
|
result: dict[str, Any] = {
|
||||||
|
"id": meal.get("metaId"),
|
||||||
|
"date": meal.get("date"),
|
||||||
|
"type": meal.get("type"),
|
||||||
|
"name": None,
|
||||||
|
"recipe_id": None,
|
||||||
|
"is_from_recipe_box": None,
|
||||||
|
"note": meal.get("note") or None,
|
||||||
|
"serves": int(raw_serves) if raw_serves is not None else None,
|
||||||
|
"can_update": rights.get("canUpdate") == "true",
|
||||||
|
"can_delete": rights.get("canDelete") == "true",
|
||||||
|
}
|
||||||
|
return json.dumps(result, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Factory
|
# Factory
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user