diff --git a/src/keeperkit/tools/_common.py b/src/keeperkit/tools/_common.py index 93ca059..a828775 100644 --- a/src/keeperkit/tools/_common.py +++ b/src/keeperkit/tools/_common.py @@ -263,7 +263,8 @@ def build_workflow_tools(client: _ClientProto) -> list[ToolSpec]: wf_type = wf.get("workflowType") or "read" chain = wf.get("chain") - descr_parts = [wf.get("description", "").strip() or wf.get("name", slug)] + raw_descr = wf.get("description") or wf.get("name") or slug + descr_parts = [str(raw_descr).strip() or slug] descr_parts.append(f"[type={wf_type}, slug='{slug}'" + (f", chain={chain}" if chain else "") + (f", price={price} USDC" if price else ", free") diff --git a/tests/test_tool_specs.py b/tests/test_tool_specs.py index 0482ac5..00b91ac 100644 --- a/tests/test_tool_specs.py +++ b/tests/test_tool_specs.py @@ -40,6 +40,26 @@ def test_build_all_combines_static_and_workflow(): assert len(all_specs) == len(STATIC_TOOL_SPECS) + len(build_workflow_tools(client)) +def test_workflow_tools_handle_null_description_and_name(): + """Real KeeperHub catalogue can return entries with description=null; + the spec builder must fall back to name/slug instead of crashing.""" + client = MockKeeperHubClient(catalogue=[ + { + "id": "wf_null_desc", + "name": None, + "description": None, + "listedSlug": "weird-workflow", + "workflowType": "read", + "priceUsdcPerCall": None, + "inputSchema": {"type": "object", "properties": {}}, + } + ]) + specs = build_workflow_tools(client) + assert len(specs) == 1 + assert specs[0].name == "keeperhub_weird_workflow" + assert "weird-workflow" in specs[0].description + + def test_safe_dispatch_handles_payment_required(): client = MockKeeperHubClient() specs = build_workflow_tools(client)