ha-mcp-dev 7.13.0.dev2037__tar.gz → 7.13.0.dev2042__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 (187) hide show
  1. {ha_mcp_dev-7.13.0.dev2037/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.13.0.dev2042}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/pyproject.toml +1 -10
  3. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_deep.py +12 -12
  4. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_fetch.py +36 -14
  5. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_scenes.py +6 -4
  6. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_automations.py +46 -33
  7. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_scenes.py +473 -386
  8. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_scripts.py +203 -145
  9. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_dev.py +242 -151
  10. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_system.py +563 -401
  11. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  12. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/LICENSE +0 -0
  13. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/MANIFEST.in +0 -0
  14. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/README.md +0 -0
  15. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/setup.cfg +0 -0
  16. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/__init__.py +0 -0
  17. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/__main__.py +0 -0
  18. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/_pypi_marker +0 -0
  19. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/_version.py +0 -0
  20. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/auth/__init__.py +0 -0
  21. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/auth/consent_form.py +0 -0
  22. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/auth/provider.py +0 -0
  23. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/backup_manager.py +0 -0
  24. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/browser_landing.py +0 -0
  25. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/__init__.py +0 -0
  26. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/rest_client.py +0 -0
  27. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/supervisor_client.py +0 -0
  28. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/websocket_client.py +0 -0
  29. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/websocket_listener.py +0 -0
  30. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/config.py +0 -0
  31. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  32. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  33. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  34. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  35. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  36. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  37. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/errors.py +0 -0
  38. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/llm_exposure.py +0 -0
  39. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/__init__.py +0 -0
  40. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/approval_queue.py +0 -0
  41. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/evaluator.py +0 -0
  42. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/handlers.py +0 -0
  43. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/middleware.py +0 -0
  44. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/model.py +0 -0
  45. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/persistence.py +0 -0
  46. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/value_sources.py +0 -0
  47. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/py.typed +0 -0
  48. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/read_only.py +0 -0
  49. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  50. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  51. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  52. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  53. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  54. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  55. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  56. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  57. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  58. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  59. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  60. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  61. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  62. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  63. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  64. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  65. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  66. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  67. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  68. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  69. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  70. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  71. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  72. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  73. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  74. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/server.py +0 -0
  75. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/__init__.py +0 -0
  76. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  77. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  78. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  79. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  80. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  81. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  82. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  83. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  84. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  85. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_theme.py +0 -0
  86. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  87. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  88. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  89. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/en.json +0 -0
  90. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  91. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/settings.css +0 -0
  92. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/settings.html +0 -0
  93. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/settings.js +0 -0
  94. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/smoke_test.py +0 -0
  95. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  96. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/strict_bps.py +0 -0
  97. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/__init__.py +0 -0
  98. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/auto_backup.py +0 -0
  99. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/backup.py +0 -0
  100. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  101. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_api.py +0 -0
  102. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_config_reads.py +0 -0
  103. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_devices.py +0 -0
  104. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_registries.py +0 -0
  105. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  106. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  107. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/device_control.py +0 -0
  108. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/enhanced.py +0 -0
  109. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/helpers.py +0 -0
  110. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/__init__.py +0 -0
  111. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/base.py +0 -0
  112. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/matter.py +0 -0
  113. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/thread.py +0 -0
  114. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  115. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/zwave.py +0 -0
  116. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/reference_validator.py +0 -0
  117. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/registry.py +0 -0
  118. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  119. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  120. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  121. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  122. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  123. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  124. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  125. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_addons.py +0 -0
  126. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_areas.py +0 -0
  127. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  128. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  129. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_calendar.py +0 -0
  130. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_camera.py +0 -0
  131. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_categories.py +0 -0
  132. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_code.py +0 -0
  133. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  134. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  135. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  136. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_energy.py +0 -0
  137. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_entities.py +0 -0
  138. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  139. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_groups.py +0 -0
  140. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_hacs.py +0 -0
  141. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_history.py +0 -0
  142. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_integrations.py +0 -0
  143. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_labels.py +0 -0
  144. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_radio.py +0 -0
  145. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_registry.py +0 -0
  146. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_resources.py +0 -0
  147. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_search.py +0 -0
  148. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_service.py +0 -0
  149. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_services.py +0 -0
  150. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_themes.py +0 -0
  151. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_todo.py +0 -0
  152. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_traces.py +0 -0
  153. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_updates.py +0 -0
  154. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_utility.py +0 -0
  155. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  156. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  157. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  158. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_zones.py +0 -0
  159. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/util_helpers.py +0 -0
  160. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/validation_middleware.py +0 -0
  161. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transforms/__init__.py +0 -0
  162. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transforms/categorized_search.py +0 -0
  163. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  164. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transport_security.py +0 -0
  165. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/update_check.py +0 -0
  166. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/__init__.py +0 -0
  167. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/config_hash.py +0 -0
  168. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/data_paths.py +0 -0
  169. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/domain_handlers.py +0 -0
  170. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  171. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  172. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/operation_manager.py +0 -0
  173. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/python_sandbox.py +0 -0
  174. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/skill_loader.py +0 -0
  175. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/usage_logger.py +0 -0
  176. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/__init__.py +0 -0
  177. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/model.py +0 -0
  178. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/persistence.py +0 -0
  179. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/resolver.py +0 -0
  180. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  181. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  182. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  183. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  184. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  185. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/tests/__init__.py +0 -0
  186. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/tests/test_constants.py +0 -0
  187. {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/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.13.0.dev2037
3
+ Version: 7.13.0.dev2042
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.13.0.dev2037"
7
+ version = "7.13.0.dev2042"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  # Upper bound widened to <3.15 so the package installs on Home Assistant's
@@ -164,15 +164,6 @@ ignore = [
164
164
  [tool.ruff.lint.per-file-ignores]
165
165
  "__init__.py" = ["F401"]
166
166
  "tests/**/*" = ["E501", "B011"]
167
- # C901 (mccabe complexity) grandfathered debt: newly enabled rule, these files
168
- # already exceeded the threshold. Remove a line once its functions are
169
- # refactored below threshold -- do not add new files here.
170
- "src/ha_mcp/tools/tools_config_automations.py" = ["C901"]
171
- "src/ha_mcp/tools/tools_config_scenes.py" = ["C901"]
172
- "src/ha_mcp/tools/tools_config_scripts.py" = ["C901"]
173
- "src/ha_mcp/tools/tools_dev.py" = ["C901"]
174
- "src/ha_mcp/tools/tools_system.py" = ["C901"]
175
- "tests/src/unit/test_advanced_settings_coverage.py" = ["C901"]
176
167
 
177
168
  [tool.pytest.ini_options]
178
169
  testpaths = ["tests"]
@@ -393,12 +393,12 @@ class DeepSearchMixin(SceneSearchMixin):
393
393
  failed_count = 0
394
394
  yaml_skipped_count = 0
395
395
  timeout_count = 0
396
- # One representative summary — only the FIRST ``failed``-class
397
- # exception is kept (the fetch closure guards the append); it rides
398
- # partial_reason as an ``e.g.`` (#1784 follow-up). The remaining
399
- # failures are counted (``failed_count``) but not summarized, so the
400
- # motivating "every per-id fetch 500s" case does N-1 fewer
401
- # ``summarize_fetch_error`` calls.
396
+ # One representative summary — ``record_first_failure`` (which holds
397
+ # the guard) keeps the first ``failed``-class exception, upgrading
398
+ # once to the first HTTP 500 so a fast-failing outlier can't suppress
399
+ # the 500 diagnosis hint; it rides partial_reason as an ``e.g.``
400
+ # (#1784 follow-up). The remaining failures are counted
401
+ # (``failed_count``) but not summarized.
402
402
  failed_errors: list[str] = []
403
403
  if not bulk_fetched:
404
404
  uids_to_fetch = [
@@ -547,12 +547,12 @@ class DeepSearchMixin(SceneSearchMixin):
547
547
  failed_count = 0
548
548
  yaml_skipped_count = 0
549
549
  timeout_count = 0
550
- # One representative summary — only the FIRST ``failed``-class
551
- # exception is kept (the fetch closure guards the append); it rides
552
- # partial_reason as an ``e.g.`` (#1784 follow-up). The remaining
553
- # failures are counted (``failed_count``) but not summarized, so the
554
- # motivating "every per-id fetch 500s" case does N-1 fewer
555
- # ``summarize_fetch_error`` calls.
550
+ # One representative summary — ``record_first_failure`` (which holds
551
+ # the guard) keeps the first ``failed``-class exception, upgrading
552
+ # once to the first HTTP 500 so a fast-failing outlier can't suppress
553
+ # the 500 diagnosis hint; it rides partial_reason as an ``e.g.``
554
+ # (#1784 follow-up). The remaining failures are counted
555
+ # (``failed_count``) but not summarized.
556
556
  failed_errors: list[str] = []
557
557
  if not bulk_fetched:
558
558
  sids_to_fetch = [
@@ -40,7 +40,8 @@ def summarize_fetch_error(exc: BaseException) -> str:
40
40
 
41
41
  HTTP errors render as ``HTTP <code>: <first line of the message>`` (the
42
42
  client's own ``API error: <code> - `` prefix is stripped rather than
43
- duplicated); everything else as ``<ExceptionType>: <first line>``.
43
+ duplicated); everything else as ``<ExceptionType>: <first line>``. An
44
+ empty message renders the bare prefix (``HTTP <code>`` / ``<Type>``).
44
45
  """
45
46
  lines = str(exc).strip().splitlines()
46
47
  text = lines[0].strip() if lines else ""
@@ -55,15 +56,24 @@ def summarize_fetch_error(exc: BaseException) -> str:
55
56
  return summary
56
57
 
57
58
 
58
- # Marks a rendered ``summarize_fetch_error`` summary whose cause the sample
59
- # cannot name: an HTTP 500's body is aiohttp's generic "500 Internal Server
60
- # Error" placeholder, so the real error (a YAMLException the per-id config
61
- # view raised, most often a ``!secret`` reference it rejects) lives only in
62
- # the HA log, never in the response.
63
- _HTTP_500_PREFIX = "HTTP 500:"
59
+ # Matches a rendered ``summarize_fetch_error`` HTTP-500 summary — ``HTTP
60
+ # 500: <text>``, or bare ``HTTP 500`` when the error body was empty. An HTTP
61
+ # 500 is the one sample whose cause the summary cannot name (the body is
62
+ # aiohttp's generic "500 Internal Server Error" placeholder; the real error —
63
+ # most often a ``!secret`` reference the per-id config view rejects — lives
64
+ # only in the HA log). Shared by the sample upgrade in
65
+ # ``record_first_failure`` and by ``http_500_diagnosis_hint`` so the two can
66
+ # never disagree on what counts as a 500.
67
+ _HTTP_500_SAMPLE = re.compile(r"HTTP 500(?::|$)")
68
+
69
+
70
+ def _is_http_500_sample(sample: str) -> bool:
71
+ """True when ``sample`` is a rendered HTTP-500 summary (``_HTTP_500_SAMPLE``)."""
72
+ return _HTTP_500_SAMPLE.match(sample) is not None
73
+
64
74
 
65
75
  # Static diagnosis appended to an HTTP-500 failed fragment. The sample names
66
- # the status but not the cause (see ``_HTTP_500_PREFIX``); this points the
76
+ # the status but not the cause (see ``_HTTP_500_SAMPLE``); this points the
67
77
  # reader at the HA log and the single most common cause, delivering the
68
78
  # five-minute diagnosis the #1784 follow-up asked for without claiming a
69
79
  # cause the response can't actually confirm. Kept endpoint-agnostic — this
@@ -84,23 +94,35 @@ def http_500_diagnosis_hint(failed_sample: str | None) -> str:
84
94
  Returns ``""`` for any other sample (or ``None``), so non-500 fragments
85
95
  are byte-identical to before. See ``_HTTP_500_DIAGNOSIS``.
86
96
  """
87
- if failed_sample and failed_sample.startswith(_HTTP_500_PREFIX):
97
+ if failed_sample and _is_http_500_sample(failed_sample):
88
98
  return _HTTP_500_DIAGNOSIS
89
99
  return ""
90
100
 
91
101
 
92
102
  def record_first_failure(failed_errors: list[str], exc: BaseException) -> None:
93
- """Capture ``exc``'s summary as the representative ``failed`` sample, once.
103
+ """Capture ``exc``'s summary as the representative ``failed`` sample.
94
104
 
95
- Only the FIRST generic-``failed`` exception per fetch pass is summarized
105
+ The first generic-``failed`` exception per fetch pass is summarized
96
106
  (subsequent ones are counted but not summarized) — in the motivating
97
107
  "every per-id fetch 500s" case that is N-1 fewer ``summarize_fetch_error``
98
- calls. The append is a no-op once ``failed_errors`` is non-empty. Called
99
- from the per-id fetch closures, which run under ``asyncio.gather`` but
100
- never suspend between the check and the append, so no lock is needed.
108
+ calls. One exception: the first HTTP 500 upgrades a non-500 sample, once —
109
+ failure order tracks latency under ``asyncio.gather``, so a fast-failing
110
+ outlier (e.g. a connection blip losing the race against slower 500
111
+ round-trips) must not claim the slot and suppress the
112
+ ``http_500_diagnosis_hint`` diagnosis this sample exists to deliver. At
113
+ most two summaries are rendered per pass. Called from the per-id fetch
114
+ closures, which run under ``asyncio.gather`` but never suspend between
115
+ the check and the write, so no lock is needed.
101
116
  """
102
117
  if not failed_errors:
103
118
  failed_errors.append(summarize_fetch_error(exc))
119
+ return
120
+ if (
121
+ isinstance(exc, HomeAssistantAPIError)
122
+ and exc.status_code == 500
123
+ and not _is_http_500_sample(failed_errors[0])
124
+ ):
125
+ failed_errors[0] = summarize_fetch_error(exc)
104
126
 
105
127
 
106
128
  def is_timeout_error(exc: BaseException) -> bool:
@@ -273,10 +273,12 @@ class SceneSearchMixin(ConfigFetchMixin):
273
273
  skipped_count = 0
274
274
  integration_skipped = 0
275
275
  timeout_count = 0
276
- # One representative summary — only the FIRST ``failed``-class
277
- # exception is kept (the fetch closure guards the append); it rides
278
- # partial_reason as an ``e.g.`` (#1784 follow-up). The remaining
279
- # failures are counted (``failed_count``) but not summarized.
276
+ # One representative summary — ``record_first_failure`` (which holds
277
+ # the guard) keeps the first ``failed``-class exception, upgrading
278
+ # once to the first HTTP 500 so a fast-failing outlier can't suppress
279
+ # the 500 diagnosis hint; it rides partial_reason as an ``e.g.``
280
+ # (#1784 follow-up). The remaining failures are counted
281
+ # (``failed_count``) but not summarized.
280
282
  failed_errors: list[str] = []
281
283
 
282
284
  # Attempt C: parallel per-id fetch with a wall-clock budget so a few
@@ -839,39 +839,7 @@ class AutomationConfigTools:
839
839
  and e.status_code == 404
840
840
  ):
841
841
  await self._raise_automation_not_found(identifier)
842
- error_text = str(e)
843
- suggestions = [
844
- "Check automation configuration format",
845
- "Ensure required fields: alias, triggers, actions",
846
- "Use entity_id format: automation.morning_routine or unique_id",
847
- "Use ha_search(domain_filter='automation') to find automations",
848
- "Use ha_get_skill_guide for automation examples",
849
- ]
850
- if isinstance(e, HomeAssistantAPIError):
851
- if "'service'" in error_text and "not allowed" in error_text:
852
- suggestions.insert(
853
- 0,
854
- "Use 'action:' not 'service:' for service calls in action steps "
855
- "(renamed in HA 2024.8).",
856
- )
857
- elif "unexpected keyword argument" in error_text.lower():
858
- suggestions.insert(
859
- 0,
860
- "An action step contains a field that belongs at the automation root "
861
- "(e.g. alias, trigger, condition). Each action step should only contain "
862
- "action/target/data/delay/choose/if/repeat/parallel keys.",
863
- )
864
- elif "'variables'" in error_text and "dictionary" in error_text:
865
- suggestions.insert(
866
- 0,
867
- "variables must be a dict mapping names to values, "
868
- 'e.g. {"variables": {"my_var": 42}}',
869
- )
870
- if bp_warnings:
871
- suggestions.append(
872
- "Config had best-practice issues that may be related: "
873
- + "; ".join(bp_warnings)
874
- )
842
+ suggestions = self._build_set_automation_suggestions(e, bp_warnings)
875
843
  error = exception_to_structured_error(
876
844
  e,
877
845
  context={"identifier": identifier},
@@ -881,6 +849,51 @@ class AutomationConfigTools:
881
849
  augment_error_dict_with_skill_content(error, bp_warnings)
882
850
  raise_tool_error(error)
883
851
 
852
+ @staticmethod
853
+ def _build_set_automation_suggestions(
854
+ e: Exception, bp_warnings: BestPracticeCheckResult
855
+ ) -> list[str]:
856
+ """Build the ordered suggestion list for a failed config-replacement set.
857
+
858
+ Extracted verbatim from ``ha_config_set_automation``'s exception
859
+ handler: HA-API messages get a targeted lead suggestion inserted, and
860
+ any best-practice warnings are appended.
861
+ """
862
+ error_text = str(e)
863
+ suggestions = [
864
+ "Check automation configuration format",
865
+ "Ensure required fields: alias, triggers, actions",
866
+ "Use entity_id format: automation.morning_routine or unique_id",
867
+ "Use ha_search(domain_filter='automation') to find automations",
868
+ "Use ha_get_skill_guide for automation examples",
869
+ ]
870
+ if isinstance(e, HomeAssistantAPIError):
871
+ if "'service'" in error_text and "not allowed" in error_text:
872
+ suggestions.insert(
873
+ 0,
874
+ "Use 'action:' not 'service:' for service calls in action steps "
875
+ "(renamed in HA 2024.8).",
876
+ )
877
+ elif "unexpected keyword argument" in error_text.lower():
878
+ suggestions.insert(
879
+ 0,
880
+ "An action step contains a field that belongs at the automation root "
881
+ "(e.g. alias, trigger, condition). Each action step should only contain "
882
+ "action/target/data/delay/choose/if/repeat/parallel keys.",
883
+ )
884
+ elif "'variables'" in error_text and "dictionary" in error_text:
885
+ suggestions.insert(
886
+ 0,
887
+ "variables must be a dict mapping names to values, "
888
+ 'e.g. {"variables": {"my_var": 42}}',
889
+ )
890
+ if bp_warnings:
891
+ suggestions.append(
892
+ "Config had best-practice issues that may be related: "
893
+ + "; ".join(bp_warnings)
894
+ )
895
+ return suggestions
896
+
884
897
  async def _upsert_automation(
885
898
  self,
886
899
  config: dict[str, Any],