fix: get_categories locale filter and list-type filter (v0.4.9)
Bug 1: all 171 category locale variants were returned; now filtered by locale parameter (default "de") → 13 German categories for shopping lists. Bug 2: TODO lists returned 171 shopping categories because sortingIndexByTaskList contains ALL list IDs regardless of type. Fix: look up list's taskListType via taskgettasklists, then match against category's taskListType. TODO lists (type=TODOS) return empty list since all API categories are SHOPPING_LIST type. sortingIndexByTaskList is explicitly documented as unreliable for type filtering in both code comments and SPEC.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -337,23 +337,40 @@ def get_tasks(list_id: str, only_open: bool = True):
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_categories(list_id: str) -> str:
|
||||
def get_categories(list_id: str, locale: str = "de") -> str:
|
||||
"""Return the task categories available for a list as JSON.
|
||||
|
||||
Categories are loaded from taskcategorysync (part of accgetallfamily) and
|
||||
filtered to those assigned to the given list via sortingIndexByTaskList.
|
||||
Shopping lists have predefined system categories (aisles); TODO lists
|
||||
typically only carry a single default system category.
|
||||
Only shopping lists (taskListType=SHOPPING_LIST) have categories. TODO
|
||||
lists return an empty list. Categories are filtered by locale so only
|
||||
the language-appropriate names are returned (default: German).
|
||||
|
||||
Note: the parameter name for assigning a category when creating or
|
||||
updating a task is not yet verified via FW_DEBUG=1. See SPEC.md.
|
||||
|
||||
Args:
|
||||
list_id: List ID from get_lists (e.g. ``taskList/23431854_29740942``).
|
||||
locale: BCP-47 language code for category names (default ``"de"``).
|
||||
Supported values seen in API: de, en, fr, es, it, nl, pt, sv,
|
||||
ru, ko, ja.
|
||||
|
||||
Returns:
|
||||
JSON list of {id, name, emoji} objects ordered by sortingIndex.
|
||||
JSON list of {id, name, emoji} objects ordered by sortingIndex,
|
||||
or an empty list when the list type has no categories.
|
||||
"""
|
||||
# Resolve the list's taskListType from taskgettasklists so we can match
|
||||
# only the categories that belong to the same list type. Non-fatal: if
|
||||
# the lookup fails we skip the type filter and return all locale matches.
|
||||
list_type: str | None = None
|
||||
try:
|
||||
list_data = _authenticated_call("taskgettasklists", {})
|
||||
raw_lists = list_data.get("a00", {}).get("r", {}).get("r", []) or []
|
||||
for lst in raw_lists:
|
||||
if lst.get("metaId") == list_id:
|
||||
list_type = lst.get("taskListType")
|
||||
break
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
data = _accgetallfamily()
|
||||
except RuntimeError as exc:
|
||||
@@ -370,18 +387,16 @@ def get_categories(list_id: str) -> str:
|
||||
indent=2,
|
||||
)
|
||||
|
||||
# Collect categories assigned to this list, ordered by their sorting index.
|
||||
# Filter by locale (exact match) and taskListType (when known).
|
||||
# sortingIndexByTaskList is NOT used for filtering: all categories are
|
||||
# assigned to all lists regardless of type, making it an unreliable signal.
|
||||
matched: list[tuple[int, dict[str, Any]]] = []
|
||||
seen: set[str] = set()
|
||||
for cat in raw_cats:
|
||||
sorting = cat.get("sortingIndexByTaskList") or {}
|
||||
if list_id not in sorting:
|
||||
if cat.get("locale") != locale:
|
||||
continue
|
||||
cat_id: str = cat.get("metaId", "")
|
||||
if cat_id in seen:
|
||||
if list_type is not None and cat.get("taskListType") != list_type:
|
||||
continue
|
||||
seen.add(cat_id)
|
||||
sort_val = int(sorting.get(list_id, cat.get("initialSortingIndex", 0)) or 0)
|
||||
sort_val = int(cat.get("initialSortingIndex") or 0)
|
||||
matched.append((sort_val, cat))
|
||||
|
||||
matched.sort(key=lambda t: t[0])
|
||||
|
||||
Reference in New Issue
Block a user