ha-mcp-dev 7.9.0.dev751__tar.gz → 7.9.0.dev753__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 (152) hide show
  1. {ha_mcp_dev-7.9.0.dev751/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.9.0.dev753}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/config.py +13 -0
  4. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/server.py +3 -1
  5. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/settings_ui/settings.js +14 -7
  6. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/auto_backup.py +12 -0
  7. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_filesystem.py +7 -1
  8. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_yaml_config.py +77 -1
  9. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  10. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/LICENSE +0 -0
  11. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/MANIFEST.in +0 -0
  12. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/README.md +0 -0
  13. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/setup.cfg +0 -0
  14. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/__init__.py +0 -0
  15. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/__main__.py +0 -0
  16. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/_pypi_marker +0 -0
  17. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/_version.py +0 -0
  18. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/auth/__init__.py +0 -0
  19. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/auth/consent_form.py +0 -0
  20. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/auth/provider.py +0 -0
  21. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/backup_manager.py +0 -0
  22. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/client/__init__.py +0 -0
  23. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/client/rest_client.py +0 -0
  24. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/client/supervisor_client.py +0 -0
  25. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/client/websocket_client.py +0 -0
  26. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/client/websocket_listener.py +0 -0
  27. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  28. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  29. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  30. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/errors.py +0 -0
  31. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/__init__.py +0 -0
  32. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/approval_queue.py +0 -0
  33. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/evaluator.py +0 -0
  34. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/handlers.py +0 -0
  35. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/middleware.py +0 -0
  36. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/model.py +0 -0
  37. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/persistence.py +0 -0
  38. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/policy/value_sources.py +0 -0
  39. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/py.typed +0 -0
  40. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/read_only.py +0 -0
  41. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  42. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  43. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  44. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  45. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  46. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  47. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  48. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  49. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  50. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  51. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  52. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  53. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  54. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  55. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  56. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  57. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  58. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  59. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  60. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  61. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  62. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  63. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/settings_ui/__init__.py +0 -0
  64. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/settings_ui/settings.css +0 -0
  65. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/settings_ui/settings.html +0 -0
  66. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/smoke_test.py +0 -0
  67. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  68. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/__init__.py +0 -0
  69. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/backup.py +0 -0
  70. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  71. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  72. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/device_control.py +0 -0
  73. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/enhanced.py +0 -0
  74. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/helpers.py +0 -0
  75. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/radio/__init__.py +0 -0
  76. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/radio/base.py +0 -0
  77. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/radio/matter.py +0 -0
  78. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/radio/thread.py +0 -0
  79. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  80. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/radio/zwave.py +0 -0
  81. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/reference_validator.py +0 -0
  82. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/registry.py +0 -0
  83. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  84. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  85. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  86. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  87. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  88. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  89. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  90. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  91. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  92. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  93. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_addons.py +0 -0
  94. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_areas.py +0 -0
  95. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  96. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  97. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_calendar.py +0 -0
  98. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_camera.py +0 -0
  99. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_categories.py +0 -0
  100. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_code.py +0 -0
  101. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  102. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  103. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  104. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  105. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  106. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  107. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_energy.py +0 -0
  108. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_entities.py +0 -0
  109. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_groups.py +0 -0
  110. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_hacs.py +0 -0
  111. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_history.py +0 -0
  112. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_integrations.py +0 -0
  113. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_labels.py +0 -0
  114. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  115. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_radio.py +0 -0
  116. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_registry.py +0 -0
  117. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_resources.py +0 -0
  118. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_search.py +0 -0
  119. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_service.py +0 -0
  120. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_services.py +0 -0
  121. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_system.py +0 -0
  122. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_themes.py +0 -0
  123. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_todo.py +0 -0
  124. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_traces.py +0 -0
  125. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_updates.py +0 -0
  126. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_utility.py +0 -0
  127. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  128. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/tools_zones.py +0 -0
  129. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/util_helpers.py +0 -0
  130. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/tools/validation_middleware.py +0 -0
  131. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/transforms/__init__.py +0 -0
  132. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/transforms/categorized_search.py +0 -0
  133. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  134. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/update_check.py +0 -0
  135. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/__init__.py +0 -0
  136. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/config_hash.py +0 -0
  137. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/data_paths.py +0 -0
  138. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/domain_handlers.py +0 -0
  139. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  140. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  141. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/operation_manager.py +0 -0
  142. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/python_sandbox.py +0 -0
  143. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/skill_loader.py +0 -0
  144. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp/utils/usage_logger.py +0 -0
  145. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  146. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  147. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  148. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  149. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  150. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/tests/__init__.py +0 -0
  151. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/tests/test_constants.py +0 -0
  152. {ha_mcp_dev-7.9.0.dev751 → ha_mcp_dev-7.9.0.dev753}/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.9.0.dev751
3
+ Version: 7.9.0.dev753
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.9.0.dev751"
7
+ version = "7.9.0.dev753"
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"
@@ -155,6 +155,17 @@ class Settings(BaseSettings):
155
155
  # files. Disabled by default; only for YAML-only features with no UI/API path.
156
156
  enable_yaml_config_editing: bool = Field(False, alias="ENABLE_YAML_CONFIG_EDITING")
157
157
 
158
+ # Two-step confirmation for ha_config_set_yaml (#1720). When on (the
159
+ # default), the first edit call returns a unified diff preview plus a
160
+ # confirm token and writes NOTHING; the edit lands only when repeated
161
+ # with that token. Sub-toggle of enable_yaml_config_editing (nested
162
+ # beneath it in the UI). Default ON deliberately: the diff preview is
163
+ # what lets the calling agent catch collateral changes before they
164
+ # reach disk. Listed in BETA_FEATURE_FIELDS purely for the addon-mode
165
+ # override path; the master-off cascade forcing it False is moot
166
+ # because the yaml tool itself is unregistered then.
167
+ enable_yaml_edit_confirm: bool = Field(True, alias="ENABLE_YAML_EDIT_CONFIRM")
168
+
158
169
  # Per-key gates for ``automation`` / ``script`` / ``scene`` under
159
170
  # ``packages/*.yaml``. The custom component accepts these three
160
171
  # PACKAGES_ONLY_YAML_KEYS unconditionally; ha-mcp's UI exposes a
@@ -604,6 +615,7 @@ FEATURE_FLAG_FIELDS: tuple[FeatureFlagField, ...] = (
604
615
  # are name-disjoint per _validate_registries()).
605
616
  FeatureFlagField("enable_mandatory_bps", "ENABLE_MANDATORY_BPS", bool),
606
617
  FeatureFlagField("enable_yaml_config_editing", "ENABLE_YAML_CONFIG_EDITING", bool),
618
+ FeatureFlagField("enable_yaml_edit_confirm", "ENABLE_YAML_EDIT_CONFIRM", bool),
607
619
  # Per-key sub-gates beneath enable_yaml_config_editing. Nested in
608
620
  # the UI, dimmed when the parent is off. Also listed in
609
621
  # BETA_FEATURE_FIELDS so they follow the same master-gate +
@@ -660,6 +672,7 @@ _FEATURE_FLAG_INT_BOUNDS: dict[str, tuple[int, int]] = {
660
672
  # gate, never by the per-field iteration.
661
673
  BETA_FEATURE_FIELDS: tuple[str, ...] = (
662
674
  "enable_yaml_config_editing",
675
+ "enable_yaml_edit_confirm", # Default-ON safety sub-toggle; see field comment.
663
676
  # Per-key sub-gates of enable_yaml_config_editing. Included here so
664
677
  # they ride the same master gate + addon-mode override path as the
665
678
  # other beta flags. Without this, the addon-mode short-circuit in
@@ -704,7 +704,9 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
704
704
  "ha_config_set_yaml": (
705
705
  "Update raw YAML in configuration.yaml or packages/*.yaml "
706
706
  "via add / replace / remove on a single top-level key "
707
- "(LAST RESORT).\n\n"
707
+ "(LAST RESORT). By default the first call returns a diff "
708
+ "preview plus confirm_token; repeat with confirm_token to "
709
+ "apply.\n\n"
708
710
  "Dedicated tools (ha_config_set_automation, "
709
711
  "ha_config_set_script, ha_config_set_scene, "
710
712
  "ha_config_set_helper) cover almost every use case and "
@@ -1511,17 +1511,21 @@ const FEATURE_META = {
1511
1511
  label: "Enable YAML config editing (beta)",
1512
1512
  help: "Beta feature, disabled by default. Allows AI assistants to add, replace, or remove top-level keys in configuration.yaml and packages/*.yaml. Only whitelisted keys are allowed (e.g., template, sensor, command_line, mqtt, knx); core keys like homeassistant, http, and recorder are blocked. Each edit validates YAML syntax, runs a config check, and creates an automatic backup. Changes to most keys require a full HA restart to take effect. See docs/beta.md for known limitations. Dedicated tools (automations, scripts, scenes, helpers, template sensors) should be preferred when available.",
1513
1513
  },
1514
+ enable_yaml_edit_confirm: {
1515
+ label: "Require confirmation for YAML edits (diff preview)",
1516
+ help: "Sub-toggle of YAML config editing, ON by default (recommended). The first ha_config_set_yaml call returns a unified diff of exactly what would change on disk plus a confirm token and writes nothing; the edit only lands when the call is repeated with that token. Exists so unintended changes outside the requested edit are caught before they reach disk (issue #1720). Turn off only to save one round-trip per edit.",
1517
+ },
1514
1518
  enable_yaml_packages_automation: {
1515
1519
  label: "Allow automation in packages/*.yaml",
1516
- help: "Sub-toggle of YAML config editing. When on, ha_config_set_yaml accepts yaml_path='automation' inside packages/*.yaml. When off, the wrapper rejects the call client-side AND the custom component rejects it server-side. Storage-mode tools (ha_config_set_automation) cover the UI-managed path and are unaffected. Disabled by default.",
1520
+ help: "Sub-toggle of YAML config editing. When on, ha_config_set_yaml accepts yaml_path='automation' inside packages/*.yaml. When off, the call is rejected both client-side and server-side. The storage-mode tool (ha_config_set_automation) is unaffected. Default OFF; only takes effect when \"Enable YAML config editing\" above is also on.",
1517
1521
  },
1518
1522
  enable_yaml_packages_script: {
1519
1523
  label: "Allow script in packages/*.yaml",
1520
- help: "Sub-toggle of YAML config editing. When on, ha_config_set_yaml accepts yaml_path='script' inside packages/*.yaml. When off, the wrapper rejects the call client-side AND the custom component rejects it server-side. Storage-mode tools (ha_config_set_script) cover the UI-managed path and are unaffected. Disabled by default.",
1524
+ help: "Sub-toggle of YAML config editing. When on, ha_config_set_yaml accepts yaml_path='script' inside packages/*.yaml. When off, the call is rejected both client-side and server-side. The storage-mode tool (ha_config_set_script) is unaffected. Default OFF; only takes effect when \"Enable YAML config editing\" above is also on.",
1521
1525
  },
1522
1526
  enable_yaml_packages_scene: {
1523
1527
  label: "Allow scene in packages/*.yaml",
1524
- help: "Sub-toggle of YAML config editing. When on, ha_config_set_yaml accepts yaml_path='scene' inside packages/*.yaml. When off, the wrapper rejects the call client-side AND the custom component rejects it server-side. Storage-mode tools (ha_config_set_scene) cover the UI-managed path and are unaffected. Disabled by default.",
1528
+ help: "Sub-toggle of YAML config editing. When on, ha_config_set_yaml accepts yaml_path='scene' inside packages/*.yaml. When off, the call is rejected both client-side and server-side. The storage-mode tool (ha_config_set_scene) is unaffected. Default OFF; only takes effect when \"Enable YAML config editing\" above is also on.",
1525
1529
  },
1526
1530
  enable_filesystem_tools: {
1527
1531
  label: "Enable filesystem tools (beta)",
@@ -1551,11 +1555,14 @@ const FEATURE_META = {
1551
1555
  // ``config.BETA_FEATURE_FIELDS`` without duplicating the name list here.
1552
1556
  let BETA_SUB_FLAGS = new Set();
1553
1557
 
1554
- // Sub-flags of ``enable_yaml_config_editing``. Rendered nested beneath
1555
- // the parent in renderFeatureFlags so the dependency is visually
1556
- // obvious. They are NOT in BETA_SUB_FLAGS — the parent is the gate,
1557
- // and the master-off parent-off cascade transitively covers them.
1558
+ // Sub-flags of ``enable_yaml_config_editing`` (confirm-flow toggle +
1559
+ // per-key packages gates). They ARE in BETA_SUB_FLAGS (it mirrors
1560
+ // config.BETA_FEATURE_FIELDS verbatim), but the main render pass skips
1561
+ // them via the includes() guard below, and renderYamlPackagesSubRows
1562
+ // re-renders them nested beneath their parent — so they never appear
1563
+ // as standalone beta-sub rows.
1558
1564
  const YAML_PACKAGES_SUB_FLAGS = [
1565
+ 'enable_yaml_edit_confirm',
1559
1566
  'enable_yaml_packages_automation',
1560
1567
  'enable_yaml_packages_script',
1561
1568
  'enable_yaml_packages_scene',
@@ -108,6 +108,7 @@ def with_auto_backup(
108
108
  id_fn: Callable[[dict[str, Any]], str] | None = None,
109
109
  client: Any = None,
110
110
  mandatory: bool = False,
111
+ skip_fn: Callable[[dict[str, Any]], bool] | None = None,
111
112
  ) -> Callable[..., Any]:
112
113
  """Decorate a write/destructive tool with pre-write auto-backup capture.
113
114
 
@@ -125,6 +126,11 @@ def with_auto_backup(
125
126
  Backup capture is best-effort: failure logs a WARNING and the wrapped
126
127
  write proceeds regardless.
127
128
 
129
+ ``skip_fn`` (optional) short-circuits the whole decorator — including
130
+ the ``mandatory`` gate — for calls it identifies as unable to write
131
+ (e.g. a yaml-edit confirm-flow preview): no snapshot, no refusal, the
132
+ wrapped tool runs directly.
133
+
128
134
  ``mandatory=True`` makes auto-backup a precondition (file/YAML writes,
129
135
  #1579 — those formerly kept their own private backups). It fails the
130
136
  write closed in two cases, both with a structured error and without
@@ -150,6 +156,12 @@ def with_auto_backup(
150
156
  def decorator(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[Any]]:
151
157
  @functools.wraps(func)
152
158
  async def wrapper(*args: Any, **kwargs: Any) -> Any:
159
+ # A call skip_fn identifies as unable to write (e.g. a yaml-edit
160
+ # confirm-flow PREVIEW, which returns a diff and writes nothing)
161
+ # needs no pre-write snapshot — and must not be refused by the
162
+ # mandatory gate either, since there is nothing to protect.
163
+ if skip_fn is not None and skip_fn(kwargs):
164
+ return await func(*args, **kwargs)
153
165
  # Settings + target resolution happen OUTSIDE the best-effort
154
166
  # ``try`` below: under ``mandatory`` a transient-tuple error from
155
167
  # ``get_global_settings`` / ``domain_fn`` / ``id_fn`` must NOT be
@@ -67,7 +67,13 @@ CALLER_TOKEN_BOOTSTRAP_SERVICE = "get_caller_token"
67
67
  # and a ``yaml_path`` arg on ``read_file``. A <0.10.0 component lacks these
68
68
  # services; the gate surfaces an actionable "update" instead of a raw
69
69
  # "service not found".
70
- MIN_COMPONENT_VERSION = "0.10.0"
70
+ # 0.11.0: the confirm-flow + diff work (#1720) adds ``require_confirm`` /
71
+ # ``confirm_token`` args to ``edit_yaml_config`` and ``diff`` / ``written``
72
+ # response fields. The server always sends ``require_confirm`` (from
73
+ # ENABLE_YAML_EDIT_CONFIRM, default on); a <0.11.0 component's strict
74
+ # (PREVENT_EXTRA) schema rejects the unknown arg with a raw voluptuous
75
+ # error — the gate surfaces an actionable "update" prompt instead.
76
+ MIN_COMPONENT_VERSION = "0.11.0"
71
77
 
72
78
 
73
79
  def _version_tuple(version: str) -> tuple[int, ...]:
@@ -63,6 +63,34 @@ _YAML_PACKAGES_FLAG_BY_KEY = {
63
63
  "scene": "enable_yaml_packages_scene",
64
64
  }
65
65
 
66
+ # Keys editable here that ALSO have a storage-mode helper equivalent.
67
+ # Deliberately NOT blocked (git-managed YAML configs are legitimate) —
68
+ # the response instead carries a reactive routing warning, since this
69
+ # exact entry point (utility_meter via raw YAML when the helper tool
70
+ # covered it) is how the #1720 corruption reached a production config.
71
+ _HELPER_EQUIVALENT_KEYS: dict[str, str] = {
72
+ "template": "template",
73
+ "utility_meter": "utility_meter",
74
+ "group": "group",
75
+ }
76
+
77
+
78
+ def _is_preview_only_call(kwargs: dict[str, Any]) -> bool:
79
+ """True when this ha_config_set_yaml call can only return a preview.
80
+
81
+ With the confirm flow enabled and no ``confirm_token`` supplied, the
82
+ component returns a diff preview and writes nothing — so the mandatory
83
+ pre-write auto-backup would snapshot for an edit that never happens
84
+ (phantom ``scope="edits"`` entries). A settings failure returns False,
85
+ failing toward capturing a backup.
86
+ """
87
+ if kwargs.get("confirm_token") is not None:
88
+ return False
89
+ try:
90
+ return bool(get_global_settings().enable_yaml_edit_confirm)
91
+ except Exception:
92
+ return False
93
+
66
94
 
67
95
  def _disabled_packages_keys(settings: Any) -> list[str]:
68
96
  """Return the sorted list of PACKAGES_ONLY_YAML_KEYS whose Settings
@@ -146,6 +174,7 @@ class YamlConfigTools:
146
174
  f"{kw.get('file') or 'configuration.yaml'}::{kw.get('yaml_path') or ''}"
147
175
  ),
148
176
  mandatory=True,
177
+ skip_fn=_is_preview_only_call,
149
178
  )
150
179
  @log_tool_usage
151
180
  async def ha_config_set_yaml(
@@ -204,6 +233,19 @@ class YamlConfigTools:
204
233
  ),
205
234
  ),
206
235
  ] = "configuration.yaml",
236
+ confirm_token: Annotated[
237
+ str | None,
238
+ Field(
239
+ default=None,
240
+ description=(
241
+ "Confirmation token from a prior preview response. When "
242
+ "the YAML edit confirmation flow is enabled (default), "
243
+ "the first call writes nothing and returns a unified "
244
+ "diff plus confirm_token; repeat the identical call "
245
+ "with that token to apply the edit."
246
+ ),
247
+ ),
248
+ ] = None,
207
249
  MandatoryBPS: Annotated[
208
250
  bool,
209
251
  Field(default=True),
@@ -249,6 +291,16 @@ class YamlConfigTools:
249
291
  live. Preserves YAML comments and HA tags (``!include``,
250
292
  ``!secret``) on round-trip; ``replace`` swaps the subtree as-is.
251
293
 
294
+ Two-step confirmation (default ON, toggle ENABLE_YAML_EDIT_CONFIRM /
295
+ Server Settings): the first call returns ``preview: true`` with a
296
+ unified ``diff`` of exactly what would change on disk and a
297
+ ``confirm_token`` — nothing is written. Review the diff for
298
+ changes outside the requested edit, then repeat the identical
299
+ call adding ``confirm_token`` to apply. Every applied write also
300
+ returns the final ``diff``. A token mismatch means the file
301
+ changed since the preview (or the token was wrong); use the
302
+ freshly returned token.
303
+
252
304
  ``template-guidelines.md`` ships in this response under ``skill_content``
253
305
  by default — YAML packages frequently include
254
306
  template sensors / command_line entities / mqtt templates, exactly where
@@ -348,9 +400,12 @@ class YamlConfigTools:
348
400
  "action": action,
349
401
  "yaml_path": yaml_path,
350
402
  "disabled_packages_keys": disabled_keys,
403
+ "require_confirm": settings.enable_yaml_edit_confirm,
351
404
  }
352
405
  if content is not None:
353
406
  service_data["content"] = content
407
+ if confirm_token is not None:
408
+ service_data["confirm_token"] = confirm_token
354
409
 
355
410
  # Call the custom component service (token injected by helper)
356
411
  result = await call_mcp_tools_service(
@@ -375,9 +430,30 @@ class YamlConfigTools:
375
430
  '(e.g. ha_reload_core(target="themes")) or restart '
376
431
  "Home Assistant to apply the change."
377
432
  )
433
+ # themes/*.yaml guard: there the top_key is a THEME NAME, so
434
+ # a theme that happens to be called 'template'/'group'/
435
+ # 'utility_meter' must not draw the helper-routing warning.
436
+ if (
437
+ action in ("add", "replace")
438
+ and top_key in _HELPER_EQUIVALENT_KEYS
439
+ and not fnmatch.fnmatch(normalized_target, "themes/*.yaml")
440
+ ):
441
+ result.setdefault("warnings", []).append(
442
+ f"Best practice: '{top_key}' has a storage-mode "
443
+ f"equivalent — ha_config_set_helper(helper_type="
444
+ f"'{_HELPER_EQUIVALENT_KEYS[top_key]}') — which avoids "
445
+ "raw YAML file rewrites entirely. Prefer it unless "
446
+ "this configuration is deliberately YAML-managed "
447
+ "(e.g. git-tracked packages)."
448
+ )
378
449
  attach_skill_content(
379
450
  result,
380
- MandatoryBPS=MandatoryBPS,
451
+ # The confirm leg (token supplied) skips the bulky skill
452
+ # payload: possession of a token proves the caller just
453
+ # received the preview response that carried it. Preview
454
+ # and single-call writes keep it — pre-write guidance is
455
+ # the whole point of MandatoryBPS.
456
+ MandatoryBPS=MandatoryBPS and confirm_token is None,
381
457
  canonical_files=_YAML_SKILL_FILES,
382
458
  referenced_files=None,
383
459
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.9.0.dev751
3
+ Version: 7.9.0.dev753
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