ha-mcp-dev 7.5.0.dev548__tar.gz → 7.5.0.dev550__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.dev548/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev550}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/backup.py +3 -1
  4. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_addons.py +2 -2
  5. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_energy.py +3 -3
  6. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_entities.py +27 -10
  7. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_integrations.py +4 -4
  8. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_search.py +1 -1
  9. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_service.py +6 -6
  10. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_system.py +7 -5
  11. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  12. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/LICENSE +0 -0
  13. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/MANIFEST.in +0 -0
  14. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/README.md +0 -0
  15. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/setup.cfg +0 -0
  16. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/__init__.py +0 -0
  17. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/__main__.py +0 -0
  18. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/_pypi_marker +0 -0
  19. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/_version.py +0 -0
  20. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/auth/__init__.py +0 -0
  21. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/auth/consent_form.py +0 -0
  22. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/auth/provider.py +0 -0
  23. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/client/__init__.py +0 -0
  24. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/client/rest_client.py +0 -0
  25. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/client/supervisor_client.py +0 -0
  26. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/client/websocket_client.py +0 -0
  27. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/client/websocket_listener.py +0 -0
  28. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/config.py +0 -0
  29. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/errors.py +0 -0
  30. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/py.typed +0 -0
  31. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  32. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  33. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  34. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  35. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  36. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  37. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  38. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  39. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  40. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  41. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  42. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  43. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  44. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  45. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  46. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  47. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  48. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  49. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  50. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  51. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/server.py +0 -0
  52. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/settings_ui.py +0 -0
  53. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/smoke_test.py +0 -0
  54. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/__init__.py +0 -0
  55. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  56. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/device_control.py +0 -0
  57. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/enhanced.py +0 -0
  58. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/helpers.py +0 -0
  59. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/reference_validator.py +0 -0
  60. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/registry.py +0 -0
  61. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/smart_search.py +0 -0
  62. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_areas.py +0 -0
  63. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  64. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  65. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_calendar.py +0 -0
  66. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_camera.py +0 -0
  67. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_categories.py +0 -0
  68. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_code.py +0 -0
  69. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  70. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  71. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  72. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  73. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  74. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  75. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  76. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_groups.py +0 -0
  77. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_hacs.py +0 -0
  78. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_history.py +0 -0
  79. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_labels.py +0 -0
  80. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  81. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_registry.py +0 -0
  82. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_resources.py +0 -0
  83. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_services.py +0 -0
  84. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_todo.py +0 -0
  85. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_traces.py +0 -0
  86. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_updates.py +0 -0
  87. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_utility.py +0 -0
  88. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  89. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  90. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/tools_zones.py +0 -0
  91. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/tools/util_helpers.py +0 -0
  92. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/transforms/__init__.py +0 -0
  93. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/transforms/categorized_search.py +0 -0
  94. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  95. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/__init__.py +0 -0
  96. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/config_hash.py +0 -0
  97. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/data_paths.py +0 -0
  98. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/domain_handlers.py +0 -0
  99. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  100. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  101. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/operation_manager.py +0 -0
  102. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/python_sandbox.py +0 -0
  103. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp/utils/usage_logger.py +0 -0
  104. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  105. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  106. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  107. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  108. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  109. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/tests/__init__.py +0 -0
  110. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/tests/test_constants.py +0 -0
  111. {ha_mcp_dev-7.5.0.dev548 → ha_mcp_dev-7.5.0.dev550}/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.dev548
3
+ Version: 7.5.0.dev550
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
@@ -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.dev548"
7
+ version = "7.5.0.dev550"
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"
@@ -450,7 +450,9 @@ async def restore_backup(
450
450
  "status": "Restore initiated - Home Assistant will restart",
451
451
  "safety_backup_id": safety_backup_id,
452
452
  "restore_database": restore_database,
453
- "warning": "Home Assistant is restarting. Connection will be temporarily lost.",
453
+ "warnings": [
454
+ "Home Assistant is restarting. Connection will be temporarily lost."
455
+ ],
454
456
  "note": "A safety backup was created before restore. You can restore from it if needed.",
455
457
  }
456
458
  else:
@@ -1272,7 +1272,7 @@ def _apply_array_ops(
1272
1272
  and inspectable
1273
1273
  and not any(field in it for it in inspectable)
1274
1274
  ):
1275
- entry["warning"] = (
1275
+ entry.setdefault("warnings", []).append(
1276
1276
  f"field {field!r} is not present on any item — "
1277
1277
  "check for a typo in the field name"
1278
1278
  )
@@ -2212,7 +2212,7 @@ def register_addon_tools(mcp: Any, client: HomeAssistantClient, **kwargs: Any) -
2212
2212
  "submitted_fields": submitted_fields,
2213
2213
  }
2214
2214
  if ignored_fields:
2215
- response["warning"] = (
2215
+ response.setdefault("warnings", []).append(
2216
2216
  f"{len(ignored_fields)} field(s) not in add-on schema were ignored "
2217
2217
  f"before write: {ignored_fields}. Use ha_get_addon(slug) to see the "
2218
2218
  "declared schema."
@@ -673,7 +673,7 @@ class EnergyTools:
673
673
  }
674
674
  if validate_warning is not None:
675
675
  response["partial"] = True
676
- response["warning"] = validate_warning
676
+ response.setdefault("warnings", []).append(validate_warning)
677
677
  return response
678
678
 
679
679
  except ToolError:
@@ -953,14 +953,14 @@ class EnergyTools:
953
953
  }
954
954
  if post_save_errors:
955
955
  response["post_save_validation_errors"] = post_save_errors
956
- response["warning"] = (
956
+ response.setdefault("warnings", []).append(
957
957
  f"Save succeeded, but the persisted config has "
958
958
  f"{len(post_save_errors)} validation error(s). Review "
959
959
  "and re-write if any relate to this change."
960
960
  )
961
961
  elif post_save_validate_error is not None:
962
962
  response["partial"] = True
963
- response["warning"] = (
963
+ response.setdefault("warnings", []).append(
964
964
  f"Save succeeded, but post-save energy/validate "
965
965
  f"failed: {post_save_validate_error}. The persisted "
966
966
  "config has not been re-validated."
@@ -376,16 +376,27 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
376
376
  _extract_ws_error(get_result),
377
377
  )
378
378
  device_rename_result = {
379
- "warning": "Entity registry lookup failed — could not determine device. Retry may succeed.",
379
+ "warnings": [
380
+ "Entity registry lookup failed — could not determine device. Retry may succeed."
381
+ ],
382
+ "lookup_failed": True,
380
383
  }
381
384
 
382
385
  device_id = (
383
386
  entity_entry.get("device_id") if not device_rename_result else None
384
387
  )
385
388
  if not device_id:
386
- device_rename_result = {
387
- "warning": "Entity has no associated device device rename skipped",
388
- }
389
+ # Only fire the "no device" warning when the registry lookup
390
+ # succeeded otherwise the L378 "lookup failed" warning
391
+ # already carries the more accurate signal, and a second
392
+ # "no associated device" claim would be unverified (we don't
393
+ # actually know what the registry says when the lookup failed).
394
+ if device_rename_result is None:
395
+ device_rename_result = {
396
+ "warnings": [
397
+ "Entity has no associated device — device rename skipped"
398
+ ],
399
+ }
389
400
  else:
390
401
  device_msg: dict[str, Any] = {
391
402
  "type": "config/device_registry/update",
@@ -397,7 +408,9 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
397
408
  device_rename_result = {"success": True, "device_id": device_id}
398
409
  else:
399
410
  device_rename_result = {
400
- "warning": f"Entity updated but device rename failed: {_extract_ws_error(device_result)}",
411
+ "warnings": [
412
+ f"Entity updated but device rename failed: {_extract_ws_error(device_result)}"
413
+ ],
401
414
  "device_id": device_id,
402
415
  }
403
416
 
@@ -511,7 +524,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
511
524
  # Include old_entity_id and rename warning when a rename was performed
512
525
  if new_entity_id is not None:
513
526
  response_data["old_entity_id"] = original_entity_id
514
- response_data["warning"] = (
527
+ response_data.setdefault("warnings", []).append(
515
528
  "Remember to update any automations, scripts, or dashboards "
516
529
  "that reference the old entity_id"
517
530
  )
@@ -521,10 +534,14 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
521
534
 
522
535
  if device_rename_result is not None:
523
536
  response_data["device_rename"] = device_rename_result
524
- # Only mark partial when device rename was attempted and failed
525
- # (not when entity simply has no device)
526
- if "warning" in device_rename_result and device_rename_result.get(
527
- "device_id"
537
+ # Mark partial when a device rename was requested but didn't complete
538
+ # for an operational reason: WS-call failure (device_id present + warnings)
539
+ # or upstream registry lookup failure (lookup_failed marker). Not partial
540
+ # when the entity simply has no device — that's a no-op, not an incomplete
541
+ # operation.
542
+ if device_rename_result.get("warnings") and (
543
+ device_rename_result.get("device_id")
544
+ or device_rename_result.get("lookup_failed")
528
545
  ):
529
546
  response_data["partial"] = True
530
547
 
@@ -1237,13 +1237,13 @@ class IntegrationTools:
1237
1237
  if res is not True
1238
1238
  ]
1239
1239
  if not_removed:
1240
- response["warning"] = (
1240
+ response.setdefault("warnings", []).append(
1241
1241
  f"Deletion confirmed but the following entities "
1242
1242
  f"are still present after the wait window: "
1243
1243
  f"{not_removed}"
1244
1244
  )
1245
1245
  if warnings:
1246
- response["warnings"] = warnings
1246
+ response.setdefault("warnings", []).extend(warnings)
1247
1247
  return response
1248
1248
 
1249
1249
  except ToolError:
@@ -1388,7 +1388,7 @@ class IntegrationTools:
1388
1388
  client, entity_id
1389
1389
  )
1390
1390
  if not removed:
1391
- response["warning"] = (
1391
+ response.setdefault("warnings", []).append(
1392
1392
  f"Deletion confirmed but {entity_id} "
1393
1393
  "is still present after the wait window."
1394
1394
  )
@@ -1531,7 +1531,7 @@ class IntegrationTools:
1531
1531
  client, entity_id
1532
1532
  )
1533
1533
  if not removed:
1534
- response["warning"] = (
1534
+ response.setdefault("warnings", []).append(
1535
1535
  f"Deletion confirmed but {entity_id} "
1536
1536
  "is still present after the wait window."
1537
1537
  )
@@ -736,7 +736,7 @@ def register_search_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
736
736
 
737
737
  # Add warning and partial flag if fallback was used
738
738
  if warning:
739
- result["warning"] = warning
739
+ result.setdefault("warnings", []).append(warning)
740
740
  result["partial"] = True
741
741
 
742
742
  return await add_timezone_metadata(client, result)
@@ -145,14 +145,14 @@ class ServiceTools:
145
145
  f"did not respond within the timeout period. The operation is likely "
146
146
  f"still running in the background."
147
147
  ),
148
- "warning": (
148
+ "warnings": [
149
149
  "Response timed out. This is normal for long-running services "
150
150
  f"like updates or firmware installs. Use ha_get_state('{entity_id}') "
151
151
  "to check the current status."
152
152
  if entity_id
153
153
  else "Response timed out. This is normal for long-running services. "
154
154
  "The service was dispatched and may still be executing."
155
- ),
155
+ ],
156
156
  }
157
157
 
158
158
  async def _capture_initial_state(self, entity_id: str | None) -> str | None:
@@ -186,11 +186,11 @@ class ServiceTools:
186
186
  if new_state:
187
187
  response["verified_state"] = new_state.get("state")
188
188
  else:
189
- response["warning"] = (
189
+ response.setdefault("warnings", []).append(
190
190
  "Service executed but state change could not be verified within timeout."
191
191
  )
192
192
  except Exception as e:
193
- response["warning"] = (
193
+ response.setdefault("warnings", []).append(
194
194
  f"Service executed but state verification failed: {e}"
195
195
  )
196
196
 
@@ -512,10 +512,10 @@ class ServiceTools:
512
512
  f"Event {event_type} was dispatched but Home Assistant "
513
513
  "did not respond within the timeout period."
514
514
  ),
515
- "warning": (
515
+ "warnings": [
516
516
  "Response timed out. The event was dispatched and may still "
517
517
  "have been delivered to subscribers."
518
- ),
518
+ ],
519
519
  }
520
520
  exception_to_structured_error(
521
521
  error,
@@ -186,10 +186,10 @@ class SystemTools:
186
186
  "Home Assistant restart initiated. "
187
187
  "The system will be unavailable for 1-5 minutes."
188
188
  ),
189
- "warning": (
189
+ "warnings": [
190
190
  "Connection will be lost during restart. "
191
191
  "Wait for Home Assistant to become available again."
192
- ),
192
+ ],
193
193
  }
194
194
 
195
195
  except ToolError:
@@ -208,7 +208,7 @@ class SystemTools:
208
208
  "Home Assistant restart initiated. "
209
209
  "Connection was closed as expected during restart."
210
210
  ),
211
- "warning": "Wait 1-5 minutes for Home Assistant to restart.",
211
+ "warnings": ["Wait 1-5 minutes for Home Assistant to restart."],
212
212
  }
213
213
 
214
214
  exception_to_structured_error(e)
@@ -299,12 +299,14 @@ class SystemTools:
299
299
  if "not found" not in error_msg.lower():
300
300
  errors.append(f"{reload_target}: {error_msg}")
301
301
 
302
- return {
302
+ response: dict[str, Any] = {
303
303
  "success": True,
304
304
  "message": f"Reloaded {len(results)} components",
305
305
  "reloaded": results,
306
- "warnings": errors if errors else None,
307
306
  }
307
+ if errors:
308
+ response["warnings"] = errors
309
+ return response
308
310
 
309
311
  else:
310
312
  # Reload specific component
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.5.0.dev548
3
+ Version: 7.5.0.dev550
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