Skip to content

Commit aa34589

Browse files
julienldclaude
andauthored
fix: add boolean coercion for string parameters from XML-style calls (#219)
* feat: add light icon variant with transparent background - Add icon-light.svg without grey background - Generate all icon sizes for both dark and light variants (16, 32, 64, 128, 256, 512) - Update main icon.png to use light/transparent version 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: add boolean coercion for string parameters from XML-style calls AI assistants using XML-style function calls pass boolean parameters as strings (e.g., "true" instead of true). This fix adds coerce_bool_param() helper and applies it to all MCP tool boolean parameters: - ha_search_entities: group_by_domain - ha_get_overview: include_state, include_entity_id - ha_bulk_control: parallel - ha_restart: confirm - ha_list_updates: include_skipped - ha_eval_template: report_errors Fixes #218 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f983929 commit aa34589

15 files changed

Lines changed: 159 additions & 19 deletions

packaging/mcpb/icon-32.png

987 Bytes
Loading

packaging/mcpb/icon-light-128.png

4.01 KB
Loading

packaging/mcpb/icon-light-16.png

595 Bytes
Loading

packaging/mcpb/icon-light-256.png

8.26 KB
Loading

packaging/mcpb/icon-light-32.png

1.09 KB
Loading

packaging/mcpb/icon-light-512.png

17.7 KB
Loading

packaging/mcpb/icon-light-64.png

2.1 KB
Loading

packaging/mcpb/icon-light.svg

Lines changed: 74 additions & 0 deletions
Loading

packaging/mcpb/icon.png

-123 KB
Loading

src/ha_mcp/tools/tools_search.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from pydantic import Field
1010

1111
from .helpers import log_tool_usage
12-
from .util_helpers import add_timezone_metadata, parse_string_list_param
12+
from .util_helpers import add_timezone_metadata, coerce_bool_param, parse_string_list_param
1313

1414

1515
def register_search_tools(mcp, client, **kwargs):
@@ -25,7 +25,7 @@ async def ha_search_entities(
2525
domain_filter: str | None = None,
2626
area_filter: str | None = None,
2727
limit: int = 10,
28-
group_by_domain: bool = False,
28+
group_by_domain: bool | str = False,
2929
) -> dict[str, Any]:
3030
"""Comprehensive entity search with fuzzy matching, domain/area filtering, and optional grouping.
3131
@@ -45,6 +45,9 @@ async def ha_search_entities(
4545
- 'minimal': Quick orientation (10 entities per domain sample) - RECOMMENDED for searches
4646
- 'standard': Complete picture (all entities, friendly names only) - for comprehensive tasks
4747
- 'full': Maximum detail (includes states, device types, services) - for deep analysis"""
48+
# Coerce boolean parameter that may come as string from XML-style calls
49+
group_by_domain_bool = coerce_bool_param(group_by_domain, "group_by_domain", default=False) or False
50+
4851
try:
4952
# If area_filter is provided, use area-based search
5053
if area_filter:
@@ -105,7 +108,7 @@ async def ha_search_entities(
105108
)
106109

107110
# Group by domain if requested
108-
if group_by_domain:
111+
if group_by_domain_bool:
109112
by_domain: dict[str, list[dict[str, Any]]] = {}
110113
for result in results:
111114
domain = result["domain"]
@@ -204,7 +207,7 @@ async def ha_search_entities(
204207
"search_type": "domain_listing",
205208
"note": f"Listing all {domain_filter} entities (empty query with domain_filter)",
206209
}
207-
if group_by_domain:
210+
if group_by_domain_bool:
208211
domain_list_data["by_domain"] = {domain_filter: results}
209212
return await add_timezone_metadata(client, domain_list_data)
210213

@@ -224,7 +227,7 @@ async def ha_search_entities(
224227
result["domain_filter"] = domain_filter
225228

226229
# Group by domain if requested
227-
if group_by_domain and "results" in result:
230+
if group_by_domain_bool and "results" in result:
228231
by_domain = {}
229232
for entity in result["results"]:
230233
domain = entity.get("domain", entity["entity_id"].split(".")[0])
@@ -273,14 +276,14 @@ async def ha_get_overview(
273276
),
274277
] = None,
275278
include_state: Annotated[
276-
bool | None,
279+
bool | str | None,
277280
Field(
278281
default=None,
279282
description="Include state field for entities (None = auto based on level). Full defaults to True.",
280283
),
281284
] = None,
282285
include_entity_id: Annotated[
283-
bool | None,
286+
bool | str | None,
284287
Field(
285288
default=None,
286289
description="Include entity_id field for entities (None = auto based on level). Full defaults to True.",
@@ -292,8 +295,12 @@ async def ha_get_overview(
292295
Returns comprehensive system information at the requested detail level.
293296
Use 'standard' (default) for most queries. Optionally customize entity fields and limits.
294297
"""
298+
# Coerce boolean parameters that may come as strings from XML-style calls
299+
include_state_bool = coerce_bool_param(include_state, "include_state", default=None)
300+
include_entity_id_bool = coerce_bool_param(include_entity_id, "include_entity_id", default=None)
301+
295302
result = await smart_tools.get_system_overview(
296-
detail_level, max_entities_per_domain, include_state, include_entity_id
303+
detail_level, max_entities_per_domain, include_state_bool, include_entity_id_bool
297304
)
298305
return cast(dict[str, Any], result)
299306

0 commit comments

Comments
 (0)