ha-mcp-dev 7.5.0.dev545__tar.gz → 7.5.0.dev547__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. {ha_mcp_dev-7.5.0.dev545/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev547}/PKG-INFO +2 -2
  2. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/pyproject.toml +2 -2
  3. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_config_automations.py +21 -7
  4. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_config_helpers.py +10 -10
  5. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_config_scenes.py +13 -22
  6. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_config_scripts.py +19 -6
  7. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_groups.py +20 -6
  8. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/util_helpers.py +6 -4
  9. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
  10. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp_dev.egg-info/requires.txt +1 -1
  11. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/LICENSE +0 -0
  12. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/MANIFEST.in +0 -0
  13. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/README.md +0 -0
  14. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/setup.cfg +0 -0
  15. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/__init__.py +0 -0
  16. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/__main__.py +0 -0
  17. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/_pypi_marker +0 -0
  18. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/_version.py +0 -0
  19. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/auth/__init__.py +0 -0
  20. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/auth/consent_form.py +0 -0
  21. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/auth/provider.py +0 -0
  22. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/client/__init__.py +0 -0
  23. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/client/rest_client.py +0 -0
  24. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/client/supervisor_client.py +0 -0
  25. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/client/websocket_client.py +0 -0
  26. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/client/websocket_listener.py +0 -0
  27. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/config.py +0 -0
  28. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/errors.py +0 -0
  29. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/py.typed +0 -0
  30. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  31. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  32. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  33. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  34. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  35. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  36. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  37. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  38. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  39. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  40. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  41. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  42. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  43. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  44. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  45. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  46. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  47. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  48. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  49. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  50. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/server.py +0 -0
  51. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/settings_ui.py +0 -0
  52. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/smoke_test.py +0 -0
  53. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/__init__.py +0 -0
  54. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/backup.py +0 -0
  55. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  56. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/device_control.py +0 -0
  57. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/enhanced.py +0 -0
  58. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/helpers.py +0 -0
  59. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/reference_validator.py +0 -0
  60. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/registry.py +0 -0
  61. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/smart_search.py +0 -0
  62. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_addons.py +0 -0
  63. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_areas.py +0 -0
  64. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  65. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  66. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_calendar.py +0 -0
  67. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_camera.py +0 -0
  68. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_categories.py +0 -0
  69. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_code.py +0 -0
  70. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  71. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  72. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_energy.py +0 -0
  73. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_entities.py +0 -0
  74. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  75. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_hacs.py +0 -0
  76. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_history.py +0 -0
  77. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_integrations.py +0 -0
  78. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_labels.py +0 -0
  79. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  80. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_registry.py +0 -0
  81. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_resources.py +0 -0
  82. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_search.py +0 -0
  83. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_service.py +0 -0
  84. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_services.py +0 -0
  85. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_system.py +0 -0
  86. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_todo.py +0 -0
  87. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_traces.py +0 -0
  88. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_updates.py +0 -0
  89. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_utility.py +0 -0
  90. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  91. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  92. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/tools/tools_zones.py +0 -0
  93. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/transforms/__init__.py +0 -0
  94. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/transforms/categorized_search.py +0 -0
  95. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  96. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/__init__.py +0 -0
  97. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/config_hash.py +0 -0
  98. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/data_paths.py +0 -0
  99. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/domain_handlers.py +0 -0
  100. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  101. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  102. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/operation_manager.py +0 -0
  103. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/python_sandbox.py +0 -0
  104. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp/utils/usage_logger.py +0 -0
  105. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  106. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  107. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  108. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  109. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/tests/__init__.py +0 -0
  110. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/tests/test_constants.py +0 -0
  111. {ha_mcp_dev-7.5.0.dev545 → ha_mcp_dev-7.5.0.dev547}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.5.0.dev545
3
+ Version: 7.5.0.dev547
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -18,7 +18,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Requires-Python: <3.14,>=3.13
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
- Requires-Dist: fastmcp==3.2.4
21
+ Requires-Dist: fastmcp==3.3.1
22
22
  Requires-Dist: httpx[socks]==0.28.1
23
23
  Requires-Dist: pydantic==2.13.4
24
24
  Requires-Dist: python-dotenv==1.2.2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.5.0.dev545"
7
+ version = "7.5.0.dev547"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -24,7 +24,7 @@ classifiers = [
24
24
  ]
25
25
 
26
26
  dependencies = [
27
- "fastmcp==3.2.4",
27
+ "fastmcp==3.3.1",
28
28
  "httpx[socks]==0.28.1",
29
29
  "pydantic==2.13.4",
30
30
  "python-dotenv==1.2.2",
@@ -12,6 +12,10 @@ from fastmcp.exceptions import ToolError
12
12
  from fastmcp.tools import tool
13
13
  from pydantic import Field
14
14
 
15
+ from ..client.rest_client import (
16
+ HomeAssistantAuthError,
17
+ HomeAssistantConnectionError,
18
+ )
15
19
  from ..errors import (
16
20
  ErrorCode,
17
21
  create_config_error,
@@ -717,7 +721,7 @@ class AutomationConfigTools:
717
721
 
718
722
  # If the client could not verify the entity was registered, warn but don't hard-fail.
719
723
  if result.get("entity_not_verified"):
720
- result["warning"] = (
724
+ result.setdefault("warnings", []).append(
721
725
  "Automation was submitted to Home Assistant but the entity was not found "
722
726
  "after polling. The automation may still have been created -- check Home "
723
727
  "Assistant logs and try reloading automations. Common causes: "
@@ -733,12 +737,18 @@ class AutomationConfigTools:
733
737
  if not entity_id and identifier and identifier.startswith("automation."):
734
738
  entity_id = identifier
735
739
  if wait_bool and entity_id:
740
+ action_word = "created" if identifier is None else "updated"
736
741
  try:
737
742
  registered = await wait_for_entity_registered(self._client, entity_id)
738
743
  if not registered:
739
- result["warning"] = f"Automation created but {entity_id} not yet queryable. It may take a moment to become available."
740
- except Exception as e:
741
- result["warning"] = f"Automation created but verification failed: {e}"
744
+ result.setdefault("warnings", []).append(
745
+ f"Automation {action_word} but {entity_id} not yet queryable. "
746
+ "It may take a moment to become available."
747
+ )
748
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
749
+ result.setdefault("warnings", []).append(
750
+ f"Automation {action_word} but verification failed: {e}"
751
+ )
742
752
 
743
753
  # Apply category to entity registry if provided
744
754
  if effective_category and entity_id:
@@ -1022,9 +1032,13 @@ class AutomationConfigTools:
1022
1032
  try:
1023
1033
  removed = await wait_for_entity_removed(self._client, entity_id_for_wait)
1024
1034
  if not removed:
1025
- result["warning"] = f"Deletion confirmed by API but {entity_id_for_wait} may still appear briefly."
1026
- except Exception as e:
1027
- result["warning"] = f"Deletion confirmed but removal verification failed: {e}"
1035
+ result.setdefault("warnings", []).append(
1036
+ f"Deletion confirmed by API but {entity_id_for_wait} may still appear briefly."
1037
+ )
1038
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
1039
+ result.setdefault("warnings", []).append(
1040
+ f"Deletion confirmed but removal verification failed: {e}"
1041
+ )
1028
1042
 
1029
1043
  return {"success": True, "action": "delete", **result}
1030
1044
  except ToolError:
@@ -1473,8 +1473,8 @@ async def _apply_registry_updates_to_entity(
1473
1473
  elif cat_result is not None:
1474
1474
  if "category" in cat_result:
1475
1475
  applied["category"] = cat_result["category"]
1476
- elif "category_warning" in cat_result:
1477
- warnings.append(f"{entity_id}: {cat_result['category_warning']}")
1476
+ elif cat_result.get("warnings"):
1477
+ warnings.extend(f"{entity_id}: {w}" for w in cat_result["warnings"])
1478
1478
 
1479
1479
  return applied
1480
1480
 
@@ -2864,7 +2864,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
2864
2864
 
2865
2865
  # Apply category via shared helper (consistent with automations/scripts).
2866
2866
  # Issue #1293: route the success/failure through ``cat_result`` so any
2867
- # ``category_warning`` lands in the top-level ``warnings`` list instead
2867
+ # category-apply failure lands in the top-level ``warnings`` list instead
2868
2868
  # of leaking nested into ``helper_data``. Mirrors the precedent in
2869
2869
  # ``_handle_flow_helper`` (the ``cat_result`` block near the end of
2870
2870
  # ``_apply_registry_updates_to_entity``).
@@ -2880,8 +2880,8 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
2880
2880
  )
2881
2881
  if "category" in cat_result:
2882
2882
  helper_data["category"] = cat_result["category"]
2883
- elif "category_warning" in cat_result:
2884
- warnings.append(cat_result["category_warning"])
2883
+ elif cat_result.get("warnings"):
2884
+ warnings.extend(cat_result["warnings"])
2885
2885
 
2886
2886
  return _helper_response(
2887
2887
  "create",
@@ -3495,7 +3495,7 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
3495
3495
  )
3496
3496
 
3497
3497
  # Apply category via shared helper. Issue #1293: route through
3498
- # ``cat_result`` so any ``category_warning`` lands in the top-level
3498
+ # ``cat_result`` so any category-apply failure lands in the top-level
3499
3499
  # ``warnings`` list instead of nested in ``updated_data``.
3500
3500
  if category:
3501
3501
  cat_result = {}
@@ -3509,8 +3509,8 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
3509
3509
  )
3510
3510
  if "category" in cat_result:
3511
3511
  updated_data["category"] = cat_result["category"]
3512
- elif "category_warning" in cat_result:
3513
- warnings.append(cat_result["category_warning"])
3512
+ elif cat_result.get("warnings"):
3513
+ warnings.extend(cat_result["warnings"])
3514
3514
 
3515
3515
  else:
3516
3516
  # Fallback for unknown/future helper types: entity registry update only
@@ -3558,8 +3558,8 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
3558
3558
  )
3559
3559
  if "category" in cat_result:
3560
3560
  updated_data["category"] = cat_result["category"]
3561
- elif "category_warning" in cat_result:
3562
- warnings.append(cat_result["category_warning"])
3561
+ elif cat_result.get("warnings"):
3562
+ warnings.extend(cat_result["warnings"])
3563
3563
 
3564
3564
  # Wait for entity to reflect the update
3565
3565
  wait_bool = coerce_bool_param(wait, "wait", default=True)
@@ -17,6 +17,7 @@ from pydantic import Field
17
17
 
18
18
  from ..client.rest_client import (
19
19
  HomeAssistantAPIError,
20
+ HomeAssistantAuthError,
20
21
  HomeAssistantConnectionError,
21
22
  )
22
23
  from ..errors import ErrorCode, create_error_response
@@ -728,16 +729,12 @@ class ConfigSceneTools:
728
729
  self._client, entity_id
729
730
  )
730
731
  if not registered:
731
- result["warning"] = (
732
+ result.setdefault("warnings", []).append(
732
733
  f"Scene updated but {entity_id} not yet queryable. "
733
734
  "It may take a moment to become available."
734
735
  )
735
- except (
736
- TimeoutError,
737
- HomeAssistantAPIError,
738
- HomeAssistantConnectionError,
739
- ) as e:
740
- result["warning"] = (
736
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
737
+ result.setdefault("warnings", []).append(
741
738
  f"Scene updated but verification failed: {e}"
742
739
  )
743
740
  if category and entity_id:
@@ -849,16 +846,14 @@ class ConfigSceneTools:
849
846
  self._client, entity_id
850
847
  )
851
848
  if not registered:
852
- result["warning"] = (
853
- f"Scene created but {entity_id} not yet queryable. "
849
+ result.setdefault("warnings", []).append(
850
+ f"Scene saved but {entity_id} not yet queryable. "
854
851
  "It may take a moment to become available."
855
852
  )
856
- except (
857
- TimeoutError,
858
- HomeAssistantAPIError,
859
- HomeAssistantConnectionError,
860
- ) as e:
861
- result["warning"] = f"Scene created but verification failed: {e}"
853
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
854
+ result.setdefault("warnings", []).append(
855
+ f"Scene saved but verification failed: {e}"
856
+ )
862
857
 
863
858
  # Apply category to entity registry if provided.
864
859
  if effective_category and entity_id:
@@ -977,15 +972,11 @@ class ConfigSceneTools:
977
972
  try:
978
973
  removed = await wait_for_entity_removed(self._client, entity_id)
979
974
  if not removed:
980
- result["warning"] = (
975
+ result.setdefault("warnings", []).append(
981
976
  f"Deletion confirmed by API but {entity_id} may still appear briefly."
982
977
  )
983
- except (
984
- TimeoutError,
985
- HomeAssistantAPIError,
986
- HomeAssistantConnectionError,
987
- ) as e:
988
- result["warning"] = (
978
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
979
+ result.setdefault("warnings", []).append(
989
980
  f"Deletion confirmed but removal verification failed: {e}"
990
981
  )
991
982
 
@@ -12,6 +12,10 @@ from fastmcp.exceptions import ToolError
12
12
  from fastmcp.tools import tool
13
13
  from pydantic import Field
14
14
 
15
+ from ..client.rest_client import (
16
+ HomeAssistantAuthError,
17
+ HomeAssistantConnectionError,
18
+ )
15
19
  from ..errors import ErrorCode, create_error_response
16
20
  from ..utils.config_hash import compute_config_hash
17
21
  from ..utils.python_sandbox import (
@@ -584,9 +588,14 @@ class ConfigScriptTools:
584
588
  try:
585
589
  registered = await wait_for_entity_registered(self._client, entity_id)
586
590
  if not registered:
587
- result["warning"] = f"Script created but {entity_id} not yet queryable. It may take a moment to become available."
588
- except Exception as e:
589
- result["warning"] = f"Script created but verification failed: {e}"
591
+ result.setdefault("warnings", []).append(
592
+ f"Script saved but {entity_id} not yet queryable. "
593
+ "It may take a moment to become available."
594
+ )
595
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
596
+ result.setdefault("warnings", []).append(
597
+ f"Script saved but verification failed: {e}"
598
+ )
590
599
 
591
600
  # Apply category to entity registry if provided
592
601
  if effective_category and entity_id:
@@ -684,9 +693,13 @@ class ConfigScriptTools:
684
693
  try:
685
694
  removed = await wait_for_entity_removed(self._client, entity_id)
686
695
  if not removed:
687
- result["warning"] = f"Deletion confirmed by API but {entity_id} may still appear briefly."
688
- except Exception as e:
689
- result["warning"] = f"Deletion confirmed but removal verification failed: {e}"
696
+ result.setdefault("warnings", []).append(
697
+ f"Deletion confirmed by API but {entity_id} may still appear briefly."
698
+ )
699
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
700
+ result.setdefault("warnings", []).append(
701
+ f"Deletion confirmed but removal verification failed: {e}"
702
+ )
690
703
 
691
704
  return {"success": True, "action": "delete", **result}
692
705
  except ToolError:
@@ -12,6 +12,10 @@ from fastmcp.exceptions import ToolError
12
12
  from fastmcp.tools import tool
13
13
  from pydantic import Field
14
14
 
15
+ from ..client.rest_client import (
16
+ HomeAssistantAuthError,
17
+ HomeAssistantConnectionError,
18
+ )
15
19
  from ..errors import ErrorCode, create_error_response
16
20
  from .helpers import (
17
21
  exception_to_structured_error,
@@ -305,12 +309,18 @@ class GroupTools:
305
309
  wait_bool = coerce_bool_param(wait, "wait", default=True)
306
310
  result: dict[str, Any] = {}
307
311
  if wait_bool:
312
+ action_word = "created" if is_create else "updated"
308
313
  try:
309
314
  registered = await wait_for_entity_registered(self._client, entity_id)
310
315
  if not registered:
311
- result["warning"] = f"Group created but {entity_id} not yet queryable. It may take a moment to become available."
312
- except Exception as e:
313
- result["warning"] = f"Group created but verification failed: {e}"
316
+ result.setdefault("warnings", []).append(
317
+ f"Group {action_word} but {entity_id} not yet queryable. "
318
+ "It may take a moment to become available."
319
+ )
320
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
321
+ result.setdefault("warnings", []).append(
322
+ f"Group {action_word} but verification failed: {e}"
323
+ )
314
324
 
315
325
  return {
316
326
  "success": True,
@@ -409,9 +419,13 @@ class GroupTools:
409
419
  try:
410
420
  removed = await wait_for_entity_removed(self._client, entity_id)
411
421
  if not removed:
412
- result["warning"] = f"Deletion confirmed by API but {entity_id} may still appear briefly."
413
- except Exception as e:
414
- result["warning"] = f"Deletion confirmed but removal verification failed: {e}"
422
+ result.setdefault("warnings", []).append(
423
+ f"Deletion confirmed by API but {entity_id} may still appear briefly."
424
+ )
425
+ except (HomeAssistantConnectionError, HomeAssistantAuthError) as e:
426
+ result.setdefault("warnings", []).append(
427
+ f"Deletion confirmed but removal verification failed: {e}"
428
+ )
415
429
 
416
430
  return {
417
431
  "success": True,
@@ -666,8 +666,10 @@ async def apply_entity_category(
666
666
  ) -> None:
667
667
  """Apply a category to an entity via the entity registry.
668
668
 
669
- Updates result_dict in-place with 'category' on success or
670
- 'category_warning' on failure.
669
+ Updates result_dict in-place: sets ``'category'`` on success, or appends
670
+ to the top-level ``'warnings'`` list on failure. The list shape mirrors
671
+ the canonical response contract documented in ``AGENTS.md`` →
672
+ *Writing MCP Tools → Return Values*.
671
673
 
672
674
  Args:
673
675
  client: HomeAssistantClient instance
@@ -695,12 +697,12 @@ async def apply_entity_category(
695
697
  else str(error_detail)
696
698
  )
697
699
  logger.warning(f"Failed to set category for {entity_id}: {error_msg}")
698
- result_dict["category_warning"] = (
700
+ result_dict.setdefault("warnings", []).append(
699
701
  f"{entity_type.capitalize()} saved but failed to set category: {error_msg}"
700
702
  )
701
703
  except Exception as e:
702
704
  logger.warning(f"Failed to set category for {entity_id}: {e}")
703
- result_dict["category_warning"] = (
705
+ result_dict.setdefault("warnings", []).append(
704
706
  f"{entity_type.capitalize()} saved but failed to set category: {e}"
705
707
  )
706
708
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.5.0.dev545
3
+ Version: 7.5.0.dev547
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -18,7 +18,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Requires-Python: <3.14,>=3.13
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
- Requires-Dist: fastmcp==3.2.4
21
+ Requires-Dist: fastmcp==3.3.1
22
22
  Requires-Dist: httpx[socks]==0.28.1
23
23
  Requires-Dist: pydantic==2.13.4
24
24
  Requires-Dist: python-dotenv==1.2.2
@@ -1,4 +1,4 @@
1
- fastmcp==3.2.4
1
+ fastmcp==3.3.1
2
2
  httpx[socks]==0.28.1
3
3
  pydantic==2.13.4
4
4
  python-dotenv==1.2.2