feat(meal-planner): add is_from_recipe_box field to get_meal_plan (v0.10.3)
Join recipeList[] from API response as a lookup table: isRecipe="true" means a real recipe from the recipe box, "false" is a free-text stub. Dish entries get is_from_recipe_box=true/false; meal entries get null. 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.10.2)
|
### Implementierte Tools (v0.10.3)
|
||||||
|
|
||||||
| Kategorie | Tools |
|
| Kategorie | Tools |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -60,7 +60,8 @@ und wird in Claude Desktop eingebunden.
|
|||||||
- v0.9.1: Bugfix reminder-Mapping (reminderUnit/reminderValue statt unit/value; value=0 ist gültig) ✓
|
- 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.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.1: get_meal_plan strukturierter Output + SPEC.md mplistinterval Response verifiziert ✓
|
||||||
- v0.10.2: get_meal_plan mealList[] einbinden (Freitext-Notizen + Portionen), merged + sortiert ✓ ← aktuell
|
- 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)
|
- 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.10.2)
|
## Features (v0.10.3)
|
||||||
|
|
||||||
### Read
|
### Read
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ MCP server for [Family Wall](https://www.familywall.com) -- read and manage your
|
|||||||
- `get_recipes` -- list all family recipes (compact summary: id, name, prep/cook time, serves)
|
- `get_recipes` -- list all family recipes (compact summary: id, name, prep/cook time, serves)
|
||||||
- `get_recipe` -- get a single recipe in full detail (ingredients, instructions, ingredients_parsed, category_ids, etc.)
|
- `get_recipe` -- get a single recipe in full detail (ingredients, instructions, ingredients_parsed, category_ids, etc.)
|
||||||
- `get_recipe_categories` -- list all available recipe categories (always returns all 5 standard categories: Bei Kindern beliebt, Wirklich einfach, Nachspeisen, Schmeckt toll, Gemüse; plus any additional categories found in existing recipes)
|
- `get_recipe_categories` -- list all available recipe categories (always returns all 5 standard categories: Bei Kindern beliebt, Wirklich einfach, Nachspeisen, Schmeckt toll, Gemüse; plus any additional categories found in existing recipes)
|
||||||
- `get_meal_plan` -- get meal plan entries for a date range (Premium feature; merges dish and meal entries, sorted by date + type; fields: id, date, type, name, recipe_id, note, serves, can_update, can_delete)
|
- `get_meal_plan` -- get meal plan entries for a date range (Premium feature; merges dish and meal entries, sorted by date + type; fields: id, date, type, name, recipe_id, is_from_recipe_box, note, serves, can_update, can_delete)
|
||||||
|
|
||||||
### Write (with confirmation prompt)
|
### Write (with confirmation prompt)
|
||||||
|
|
||||||
|
|||||||
@@ -696,7 +696,10 @@ a00.r.r
|
|||||||
.sortingIndex → Sortierung (numerischer Timestamp als String)
|
.sortingIndex → Sortierung (numerischer Timestamp als String)
|
||||||
.rights.canUpdate → "true" wenn bearbeitbar
|
.rights.canUpdate → "true" wenn bearbeitbar
|
||||||
.rights.canDelete → "true" wenn löschbar
|
.rights.canDelete → "true" wenn löschbar
|
||||||
.recipeList[] → vollständige Rezept-Objekte der verknüpften Rezepte
|
.recipeList[] → Rezept-Objekte der verknüpften Gerichte
|
||||||
|
.metaId → Rezept-ID (z.B. "recipe/16282169_7932720")
|
||||||
|
.isRecipe → "true" = echtes Rezept aus der Rezeptbox (hat Zutaten etc.)
|
||||||
|
"false" = Freitext-Stub (nur Name, keine Zutaten)
|
||||||
.mealList[] → Freitext-Notizen (meal-Objekte)
|
.mealList[] → Freitext-Notizen (meal-Objekte)
|
||||||
.metaId → "meal/<family_num>_<meal_num>"
|
.metaId → "meal/<family_num>_<meal_num>"
|
||||||
.date → Datum der Mahlzeit (z.B. "2026-04-17")
|
.date → Datum der Mahlzeit (z.B. "2026-04-17")
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "mcp-familywall"
|
name = "mcp-familywall"
|
||||||
version = "0.10.2"
|
version = "0.10.3"
|
||||||
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.10.2"
|
__version__ = "0.10.3"
|
||||||
|
|||||||
@@ -2236,6 +2236,9 @@ def get_meal_plan(date_from: str, date_to: str) -> str:
|
|||||||
- ``type`` — ``BREAKFAST``, ``LUNCH``, ``SNACK``, or ``DINNER``
|
- ``type`` — ``BREAKFAST``, ``LUNCH``, ``SNACK``, or ``DINNER``
|
||||||
- ``name`` — dish name, or ``null`` for meal entries
|
- ``name`` — dish name, or ``null`` for meal entries
|
||||||
- ``recipe_id`` — linked recipe metaId, or ``null``
|
- ``recipe_id`` — linked recipe metaId, or ``null``
|
||||||
|
- ``is_from_recipe_box`` — ``true`` if the linked recipe was pulled from the
|
||||||
|
recipe box; ``false`` if it is a free-text stub (name only, no ingredients);
|
||||||
|
``null`` for meal entries or when no recipe is linked
|
||||||
- ``note`` — free-text note (meal entries only), or ``null``
|
- ``note`` — free-text note (meal entries only), or ``null``
|
||||||
- ``serves`` — number of servings as int (meal entries only), or ``null``
|
- ``serves`` — number of servings as int (meal entries only), or ``null``
|
||||||
- ``can_update`` — whether the entry can be updated
|
- ``can_update`` — whether the entry can be updated
|
||||||
@@ -2261,6 +2264,16 @@ def get_meal_plan(date_from: str, date_to: str) -> str:
|
|||||||
|
|
||||||
raw_dishes: list[dict[str, Any]] = payload.get("list") or []
|
raw_dishes: list[dict[str, Any]] = payload.get("list") or []
|
||||||
raw_meals: list[dict[str, Any]] = payload.get("mealList") or []
|
raw_meals: list[dict[str, Any]] = payload.get("mealList") or []
|
||||||
|
raw_recipe_list: list[dict[str, Any]] = payload.get("recipeList") or []
|
||||||
|
|
||||||
|
# Build lookup: recipeId → is_from_recipe_box
|
||||||
|
# recipeList[] contains one entry per linked recipe; isRecipe="true" means it is
|
||||||
|
# a real recipe from the recipe box, "false" means a free-text stub (name only).
|
||||||
|
recipe_lookup: dict[str, bool] = {
|
||||||
|
r["metaId"]: r.get("isRecipe") == "true"
|
||||||
|
for r in raw_recipe_list
|
||||||
|
if isinstance(r, dict) and "metaId" in r
|
||||||
|
}
|
||||||
|
|
||||||
_type_order = {"BREAKFAST": 0, "LUNCH": 1, "SNACK": 2, "DINNER": 3}
|
_type_order = {"BREAKFAST": 0, "LUNCH": 1, "SNACK": 2, "DINNER": 3}
|
||||||
|
|
||||||
@@ -2268,13 +2281,15 @@ def get_meal_plan(date_from: str, date_to: str) -> str:
|
|||||||
|
|
||||||
for dish in raw_dishes:
|
for dish in raw_dishes:
|
||||||
rights = dish.get("rights") or {}
|
rights = dish.get("rights") or {}
|
||||||
|
recipe_id: str | None = dish.get("recipeId") or None
|
||||||
result.append(
|
result.append(
|
||||||
{
|
{
|
||||||
"id": dish.get("metaId"),
|
"id": dish.get("metaId"),
|
||||||
"date": dish.get("date"),
|
"date": dish.get("date"),
|
||||||
"type": dish.get("type"),
|
"type": dish.get("type"),
|
||||||
"name": dish.get("name"),
|
"name": dish.get("name"),
|
||||||
"recipe_id": dish.get("recipeId") or None,
|
"recipe_id": recipe_id,
|
||||||
|
"is_from_recipe_box": recipe_lookup.get(recipe_id) if recipe_id else None,
|
||||||
"note": None,
|
"note": None,
|
||||||
"serves": None,
|
"serves": None,
|
||||||
"can_update": rights.get("canUpdate") == "true",
|
"can_update": rights.get("canUpdate") == "true",
|
||||||
@@ -2292,6 +2307,7 @@ def get_meal_plan(date_from: str, date_to: str) -> str:
|
|||||||
"type": meal.get("type"),
|
"type": meal.get("type"),
|
||||||
"name": None,
|
"name": None,
|
||||||
"recipe_id": None,
|
"recipe_id": None,
|
||||||
|
"is_from_recipe_box": None,
|
||||||
"note": meal.get("note") or None,
|
"note": meal.get("note") or None,
|
||||||
"serves": int(raw_serves) if raw_serves is not None else None,
|
"serves": int(raw_serves) if raw_serves is not None else None,
|
||||||
"can_update": rights.get("canUpdate") == "true",
|
"can_update": rights.get("canUpdate") == "true",
|
||||||
|
|||||||
Reference in New Issue
Block a user