diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a867c3..a873a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This project follows Semantic Versioning (SemVer). Breaking changes (removed tools, changed parameters) increment the major version. +## [1.4.3] – 2026-04-18 + +### Improved +- **Usability: ID resolution in docstrings** — enforce transparent display of human-readable names: + - `get_tasks`: Document required member/category name resolution before user display + - `get_lists`: Document required circle name resolution before user display + - `get_activities`: Document required author name resolution before user display + - `get_wall_posts`: Document required author name resolution before user display + - `get_members`: Added proactive call guidance before presenting member-ID data + - `get_circles`: Added proactive call guidance before presenting circle-ID data + - `get_categories`: Added proactive call guidance for shopping list tasks + - `get_meal_plan`: Document required recipe name resolution for recipe_id before user display + - `CLAUDE.md`: Added general usability rule at top of implementation section + +### Notes +- No breaking changes; docstring enhancements only +- All tools remain backward compatible +- Ensures consistent UX: no raw numeric/metaIDs shown to users in any context + ## [1.4.2] – 2026-04-18 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 9b3be70..0d80f26 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,7 +27,7 @@ und wird in Claude Desktop eingebunden. ## Aktueller Stand -### Version: **v1.3.0** ← aktuell +### Version: **v1.4.3** ← aktuell ### Implementierte Tools @@ -97,6 +97,9 @@ können den echten Request-Body in diesen Fällen nicht sehen. ## 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, diff --git a/pyproject.toml b/pyproject.toml index 3f9422a..377c77f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mcp-familywall" -version = "1.4.2" +version = "1.4.3" description = "MCP server for Family Wall — manage your family's circles, lists, tasks, recipes, and meal plan via Claude" readme = "README.md" requires-python = ">=3.12" diff --git a/src/mcp_familywall/server.py b/src/mcp_familywall/server.py index ca99e8a..05dafbe 100644 --- a/src/mcp_familywall/server.py +++ b/src/mcp_familywall/server.py @@ -146,7 +146,10 @@ def _extract_tasks(data: dict[str, Any]) -> list[dict[str, Any]]: @mcp.tool() def get_circles() -> str: - """Return all Family Wall circles as JSON list of {id, name}.""" + """Return all Family Wall circles as JSON list of {id, name}. + + Call this proactively before presenting any data that contains circle IDs to the user. + """ try: raw_circles = _famlistfamily() except RuntimeError as exc: @@ -220,6 +223,9 @@ def _famlistfamily() -> list[dict[str, Any]]: def get_members(circle_id: str | None = None) -> str: """Return Family Wall circle members as JSON, optionally filtered by circle. + Call this proactively before presenting any data that contains member IDs + (assignee_ids, author_id, creator) to the user. + Args: circle_id: Optional circle ID from get_circles (e.g. ``family/23431854``). When omitted all members across all circles are returned. @@ -311,6 +317,9 @@ def get_lists(scope: str | None = None) -> str: Returns: JSON list of list objects with keys id, name, type, open, total, emoji, color, circle_id. + + IMPORTANT: When presenting lists to the user, always resolve + circle_id to the circle name via get_circles. Never show raw IDs. """ try: email, password = get_credentials() @@ -419,6 +428,12 @@ def get_tasks(list_id: str, only_open: bool = True) -> str: JSON array of task objects with keys: id, text, description, completed, category_id, due_date, assignee_ids, recurrency, recurrency_interval, rrule, reminder. + + IMPORTANT: Before presenting results to the user, always resolve: + - assignee_ids → member names via get_members + - category_id → category name via get_categories + - circle context → circle name from get_lists + Never show raw IDs to the user. """ # Extract circle number from list_id format: taskList/_ try: @@ -541,6 +556,9 @@ def get_categories(list_id: str, locale: str = "de") -> str: Use the returned ``id`` values as the ``category_id`` parameter in ``create_task`` and ``update_task``. + Call this proactively before presenting shopping list tasks to the user so + category_id values can be shown as readable names. + Args: list_id: List ID from get_lists (e.g. ``taskList/23431854_29740942``). locale: BCP-47 language code for category names (default ``"de"``). @@ -772,6 +790,9 @@ def get_activities(limit: int = 20) -> str: Returns: JSON list of activity objects sorted by date descending, including wall, task, and taskList activity entries. + + IMPORTANT: Before presenting to the user, resolve author_id to + the member name via get_members. Never show raw IDs. """ try: email, password = get_credentials() @@ -2035,6 +2056,9 @@ def get_wall_posts(limit: int = 20) -> str: JSON list of post objects with keys: id, type, text, date, author, author_id, liked_by_me, like_count, comment_count. + + IMPORTANT: Before presenting to the user, resolve author_id to + the member name via get_members. Never show raw IDs. """ try: email, password = get_credentials() @@ -2776,6 +2800,9 @@ def get_meal_plan(date_from: str, date_to: str) -> str: - ``can_update`` — whether the entry can be updated - ``can_delete`` — whether the entry can be deleted + IMPORTANT: When recipe_id is present, use get_recipe to resolve it to the + recipe name for display. Never show raw recipe IDs. + Returns an error message string on failure. """ if date_err := _validate_date(date_from):