From 0e34b067e6ae6a70bb4df19cccc1906b78fe5315 Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Sat, 18 Apr 2026 00:03:38 +0200 Subject: [PATCH] fix(get_tasks): support for secondary circles by deriving scope from list_id (v1.4.2) --- CHANGELOG.md | 12 ++++++++++++ pyproject.toml | 2 +- src/mcp_familywall/server.py | 38 +++++++++++++++++++++++++++++------- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a76f1..1a867c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,18 @@ 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.2] – 2026-04-18 + +### Fixed +- **`get_tasks` support for secondary circles** — lists in non-primary circles now return tasks correctly + - Circle is automatically derived from `list_id` format (`taskList/_`) + - Scope parameter passed to `accgetallfamily` call; matches `get_lists` pattern + - Docstring updated to clarify circle support and list_id format requirement + +### Notes +- `_accgetallfamily()` now accepts optional `scope` parameter for secondary circle support +- No breaking changes; all existing code remains compatible + ## [1.4.1] – 2026-04-17 ### Improved diff --git a/pyproject.toml b/pyproject.toml index 36466ec..3f9422a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mcp-familywall" -version = "1.4.1" +version = "1.4.2" 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 ff8ee9a..ca99e8a 100644 --- a/src/mcp_familywall/server.py +++ b/src/mcp_familywall/server.py @@ -44,9 +44,13 @@ def _validate_date(date: str) -> str | None: return None -def _accgetallfamily() -> dict[str, Any]: +def _accgetallfamily(scope: str | None = None) -> dict[str, Any]: """Login, call accgetallfamily, logout and return the response body. + Args: + scope: Optional circle metaId (e.g. "family/16473836") to fetch data for + a specific circle. When None (default), fetches data for the primary circle. + Raises: RuntimeError: On credential or API errors. """ @@ -58,10 +62,10 @@ def _accgetallfamily() -> dict[str, Any]: try: with FamilyWallClient() as client: client.login(email, password) - data = client.call( - "accgetallfamily", - {"a01call": "taskcategorysync", "a02call": "tasksync"}, - ) + params: dict[str, Any] = {"a01call": "taskcategorysync", "a02call": "tasksync"} + if scope: + params["scope"] = scope + data = client.call("accgetallfamily", params) client.logout() return data except FamilyWallError as exc: @@ -402,9 +406,29 @@ def get_lists(scope: str | None = None) -> str: @mcp.tool() def get_tasks(list_id: str, only_open: bool = True) -> str: - """Return tasks for a list as JSON. list_id from get_lists. only_open=True filters completed.""" + """Return tasks for a list as JSON. + + Lists from all circles are supported. The circle is automatically derived from + the list_id — always use IDs from get_lists, never construct them manually. + + Args: + list_id: List ID from get_lists (e.g. ``taskList/23431854_29740942``). + only_open: When True (default), filter to incomplete tasks only. + + Returns: + JSON array of task objects with keys: id, text, description, completed, + category_id, due_date, assignee_ids, recurrency, recurrency_interval, + rrule, reminder. + """ + # Extract circle number from list_id format: taskList/_ try: - data = _accgetallfamily() + circle_num = list_id.split("/")[1].split("_")[0] + scope = f"family/{circle_num}" + except (IndexError, ValueError): + return _err(f"Invalid list_id format: {list_id!r}") + + try: + data = _accgetallfamily(scope) except RuntimeError as exc: return _err(str(exc))