ha-mcp-dev 7.14.2.dev2091__tar.gz → 7.14.2.dev2095__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 (188) hide show
  1. {ha_mcp_dev-7.14.2.dev2091/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.14.2.dev2095}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/paths.py +66 -40
  4. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/config_entry_flow.py +156 -48
  5. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_bug_report.py +36 -4
  6. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_dashboards.py +165 -36
  7. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  8. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/LICENSE +0 -0
  9. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/MANIFEST.in +0 -0
  10. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/README.md +0 -0
  11. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/setup.cfg +0 -0
  12. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/__init__.py +0 -0
  13. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/__main__.py +0 -0
  14. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/_pypi_marker +0 -0
  15. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/_version.py +0 -0
  16. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/auth/__init__.py +0 -0
  17. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/auth/consent_form.py +0 -0
  18. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/auth/provider.py +0 -0
  19. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/backup_manager.py +0 -0
  20. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/browser_landing.py +0 -0
  21. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/__init__.py +0 -0
  22. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/rest_client.py +0 -0
  23. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/supervisor_client.py +0 -0
  24. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/websocket_client.py +0 -0
  25. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/websocket_listener.py +0 -0
  26. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/config.py +0 -0
  27. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  28. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  29. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  30. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  31. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  32. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/errors.py +0 -0
  33. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/llm_exposure.py +0 -0
  34. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/__init__.py +0 -0
  35. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/approval_queue.py +0 -0
  36. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/evaluator.py +0 -0
  37. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/handlers.py +0 -0
  38. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/middleware.py +0 -0
  39. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/model.py +0 -0
  40. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/persistence.py +0 -0
  41. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/value_sources.py +0 -0
  42. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/py.typed +0 -0
  43. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/read_only.py +0 -0
  44. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  45. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  46. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  47. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  48. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  49. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  50. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  51. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  52. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  53. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  54. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  55. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  56. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  57. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  58. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  59. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  60. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  61. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  62. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  63. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  64. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  65. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  66. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  67. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  68. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  69. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/server.py +0 -0
  70. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/__init__.py +0 -0
  71. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  72. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  73. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  74. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  75. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  76. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  77. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  78. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  79. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  80. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_theme.py +0 -0
  81. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  82. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  83. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  84. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/en.json +0 -0
  85. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  86. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
  87. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/settings.css +0 -0
  88. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/settings.html +0 -0
  89. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/settings.js +0 -0
  90. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/smoke_test.py +0 -0
  91. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  92. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/strict_bps.py +0 -0
  93. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/__init__.py +0 -0
  94. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/auto_backup.py +0 -0
  95. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/backup.py +0 -0
  96. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  97. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_api.py +0 -0
  98. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_config_reads.py +0 -0
  99. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_devices.py +0 -0
  100. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_registries.py +0 -0
  101. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  102. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/device_control.py +0 -0
  103. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/enhanced.py +0 -0
  104. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/helpers.py +0 -0
  105. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/__init__.py +0 -0
  106. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/base.py +0 -0
  107. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/matter.py +0 -0
  108. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/thread.py +0 -0
  109. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  110. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/zwave.py +0 -0
  111. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/reference_validator.py +0 -0
  112. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/registry.py +0 -0
  113. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  114. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  115. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  116. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  117. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  118. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  119. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  120. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  121. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  122. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  123. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_addons.py +0 -0
  124. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_areas.py +0 -0
  125. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  126. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_calendar.py +0 -0
  127. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_camera.py +0 -0
  128. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_categories.py +0 -0
  129. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_code.py +0 -0
  130. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  131. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  132. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  133. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  134. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  135. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_dev.py +0 -0
  136. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_energy.py +0 -0
  137. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_entities.py +0 -0
  138. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  139. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_groups.py +0 -0
  140. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_hacs.py +0 -0
  141. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_history.py +0 -0
  142. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_integrations.py +0 -0
  143. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_labels.py +0 -0
  144. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_radio.py +0 -0
  145. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_registry.py +0 -0
  146. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_resources.py +0 -0
  147. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_search.py +0 -0
  148. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_service.py +0 -0
  149. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_services.py +0 -0
  150. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_system.py +0 -0
  151. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_themes.py +0 -0
  152. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_todo.py +0 -0
  153. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_traces.py +0 -0
  154. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_updates.py +0 -0
  155. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_utility.py +0 -0
  156. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  157. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  158. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  159. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_zones.py +0 -0
  160. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/util_helpers.py +0 -0
  161. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/validation_middleware.py +0 -0
  162. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transforms/__init__.py +0 -0
  163. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transforms/categorized_search.py +0 -0
  164. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  165. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transport_security.py +0 -0
  166. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/update_check.py +0 -0
  167. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/__init__.py +0 -0
  168. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/config_hash.py +0 -0
  169. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/data_paths.py +0 -0
  170. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/domain_handlers.py +0 -0
  171. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  172. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  173. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/operation_manager.py +0 -0
  174. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/python_sandbox.py +0 -0
  175. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/skill_loader.py +0 -0
  176. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/usage_logger.py +0 -0
  177. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/__init__.py +0 -0
  178. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/model.py +0 -0
  179. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/persistence.py +0 -0
  180. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/resolver.py +0 -0
  181. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  182. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  183. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  184. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  185. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  186. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/tests/__init__.py +0 -0
  187. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/tests/test_constants.py +0 -0
  188. {ha_mcp_dev-7.14.2.dev2091 → ha_mcp_dev-7.14.2.dev2095}/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.14.2.dev2091
3
+ Version: 7.14.2.dev2095
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.14.2.dev2091"
7
+ version = "7.14.2.dev2095"
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
@@ -347,42 +347,28 @@ async def fetch_dashboard_render_config(
347
347
  return cast(dict[str, Any], config)
348
348
 
349
349
 
350
- def resolve_dashboard_view(
350
+ def match_dashboard_view(
351
351
  dashboard_url_path: str,
352
352
  config: dict[str, Any],
353
- view_path: str | None,
354
- ) -> DashboardRenderTarget:
355
- """Resolve one named Lovelace view to a canonical frontend route."""
356
- base_path = dashboard_frontend_path(dashboard_url_path)
357
- if view_path is None:
358
- views = config.get("views")
359
- has_static_views = isinstance(views, list) and bool(views)
360
- if "strategy" in config:
361
- warning = (
362
- "No view_path was supplied; this strategy dashboard generates "
363
- "its views at runtime, so only the base route is available."
364
- )
365
- elif has_static_views:
366
- warning = (
367
- "No view_path was supplied; the dashboard base route renders "
368
- "the first view visible to Puppet's Home Assistant user."
369
- )
370
- else:
371
- warning = (
372
- "No view_path was supplied and the dashboard has no static views; "
373
- "only the base route is available."
374
- )
375
- return DashboardRenderTarget(
376
- dashboard_url_path=base_path,
377
- view_path=None,
378
- render_path=base_path,
379
- view_index=None,
380
- stable=False,
381
- warnings=(warning,),
382
- )
383
-
384
- cleaned_view_path = view_path
385
- if not cleaned_view_path.strip():
353
+ view_path: str,
354
+ *,
355
+ strategy_suggestions: tuple[str, ...] = (
356
+ "Use dashboard_path with the frontend route instead",
357
+ ),
358
+ ) -> tuple[int, dict[str, Any]]:
359
+ """Match one named Lovelace view by its configured ``views[].path``.
360
+
361
+ Shared by screenshot view resolution and get-mode view scoping so both
362
+ honour identical semantics: exact match on the configured path, with
363
+ structured errors for an empty path, a strategy dashboard (no static
364
+ views to match), and a missing/ambiguous path (the not-found error lists
365
+ the available paths). Returns ``(view_index, view)``.
366
+
367
+ ``strategy_suggestions`` lets each caller phrase the strategy-dashboard
368
+ remediation in its own vocabulary (the screenshot tools have a
369
+ ``dashboard_path`` route parameter; config scoping does not).
370
+ """
371
+ if not view_path.strip():
386
372
  raise_tool_error(
387
373
  create_error_response(
388
374
  ErrorCode.VALIDATION_INVALID_PARAMETER,
@@ -397,9 +383,9 @@ def resolve_dashboard_view(
397
383
  "Strategy dashboards do not expose static named view paths.",
398
384
  context={
399
385
  "dashboard_url_path": dashboard_url_path,
400
- "view_path": cleaned_view_path,
386
+ "view_path": view_path,
401
387
  },
402
- suggestions=["Use dashboard_path with the frontend route instead"],
388
+ suggestions=list(strategy_suggestions),
403
389
  )
404
390
  )
405
391
  views = config.get("views")
@@ -408,7 +394,7 @@ def resolve_dashboard_view(
408
394
  matches = [
409
395
  (index, view)
410
396
  for index, view in enumerate(views)
411
- if _configured_view_path(view) == cleaned_view_path
397
+ if _configured_view_path(view) == view_path
412
398
  ]
413
399
  if len(matches) != 1:
414
400
  available = [
@@ -420,17 +406,57 @@ def resolve_dashboard_view(
420
406
  raise_tool_error(
421
407
  create_error_response(
422
408
  ErrorCode.RESOURCE_NOT_FOUND,
423
- f"Dashboard view path '{cleaned_view_path}' is {reason}.",
409
+ f"Dashboard view path '{view_path}' is {reason}.",
424
410
  context={
425
411
  "dashboard_url_path": dashboard_url_path,
426
- "view_path": cleaned_view_path,
412
+ "view_path": view_path,
427
413
  "available_view_paths": available,
428
414
  },
429
415
  suggestions=["Use a view_path returned by ha_config_get_dashboard"],
430
416
  )
431
417
  )
418
+ return matches[0]
419
+
432
420
 
433
- view_index, _ = matches[0]
421
+ def resolve_dashboard_view(
422
+ dashboard_url_path: str,
423
+ config: dict[str, Any],
424
+ view_path: str | None,
425
+ ) -> DashboardRenderTarget:
426
+ """Resolve one named Lovelace view to a canonical frontend route."""
427
+ base_path = dashboard_frontend_path(dashboard_url_path)
428
+ if view_path is None:
429
+ views = config.get("views")
430
+ has_static_views = isinstance(views, list) and bool(views)
431
+ if "strategy" in config:
432
+ warning = (
433
+ "No view_path was supplied; this strategy dashboard generates "
434
+ "its views at runtime, so only the base route is available."
435
+ )
436
+ elif has_static_views:
437
+ warning = (
438
+ "No view_path was supplied; the dashboard base route renders "
439
+ "the first view visible to Puppet's Home Assistant user."
440
+ )
441
+ else:
442
+ warning = (
443
+ "No view_path was supplied and the dashboard has no static views; "
444
+ "only the base route is available."
445
+ )
446
+ return DashboardRenderTarget(
447
+ dashboard_url_path=base_path,
448
+ view_path=None,
449
+ render_path=base_path,
450
+ view_index=None,
451
+ stable=False,
452
+ warnings=(warning,),
453
+ )
454
+
455
+ view_index, _ = match_dashboard_view(dashboard_url_path, config, view_path)
456
+ cleaned_view_path = view_path
457
+ views = config.get("views")
458
+ if not isinstance(views, list):
459
+ views = []
434
460
  render_path = _safe_named_render_path(base_path, cleaned_view_path)
435
461
  if render_path is None:
436
462
  return _fallback_view_target(
@@ -11,6 +11,7 @@ for the 15 helper types listed in FLOW_HELPER_TYPES.
11
11
  """
12
12
 
13
13
  import asyncio
14
+ import copy
14
15
  import logging
15
16
  from collections.abc import Iterator
16
17
  from enum import StrEnum
@@ -78,6 +79,7 @@ _RECONFIGURE_SUCCESS_REASONS = frozenset(
78
79
  "reconfigure_successful",
79
80
  }
80
81
  )
82
+ _MISSING_DEFAULT = object()
81
83
 
82
84
 
83
85
  class _FlowType(StrEnum):
@@ -172,6 +174,47 @@ def _record_ignored_section_keys(
172
174
  )
173
175
 
174
176
 
177
+ def _field_default_value(field: dict[str, Any]) -> Any:
178
+ """Return a serialized schema field's default/suggested value, if present."""
179
+ description = field.get("description")
180
+ if isinstance(description, dict) and description.get("suggested_value") is not None:
181
+ return copy.deepcopy(description["suggested_value"])
182
+ if field.get("suggested_value") is not None:
183
+ return copy.deepcopy(field["suggested_value"])
184
+ if "default" in field:
185
+ return copy.deepcopy(field["default"])
186
+ return _MISSING_DEFAULT
187
+
188
+
189
+ def _schema_default_values(data_schema: list[Any]) -> dict[str, Any]:
190
+ """Build default form data from serialized schema suggestions/defaults."""
191
+ defaults: dict[str, Any] = {}
192
+ for field in data_schema:
193
+ if not isinstance(field, dict):
194
+ continue
195
+ name = field.get("name")
196
+ nested_schema = field.get("schema")
197
+ if not isinstance(name, str):
198
+ continue
199
+ if isinstance(nested_schema, list):
200
+ nested_defaults = _schema_default_values(nested_schema)
201
+ if nested_defaults:
202
+ defaults[name] = nested_defaults
203
+ continue
204
+ default = _field_default_value(field)
205
+ if default is not _MISSING_DEFAULT:
206
+ defaults[name] = default
207
+ return defaults
208
+
209
+
210
+ def _required_section_defaults(field: dict[str, Any]) -> dict[str, Any]:
211
+ """Return default data for a required section, otherwise an empty dict."""
212
+ nested_schema = field.get("schema")
213
+ if not field.get("required") or not isinstance(nested_schema, list):
214
+ return {}
215
+ return _schema_default_values(nested_schema)
216
+
217
+
175
218
  def _ignored_keys_warnings(
176
219
  ignored_config_keys: set[str], remaining_config: dict[str, Any]
177
220
  ) -> list[str]:
@@ -194,10 +237,89 @@ def _ignored_keys_warnings(
194
237
  return warnings
195
238
 
196
239
 
240
+ def _consume_section_schema(
241
+ field: dict[str, Any],
242
+ explicit_section: dict[str, Any] | None,
243
+ remaining_config: dict[str, Any],
244
+ ignored_config_keys: set[str] | None,
245
+ consumed_config_keys: set[str] | None,
246
+ path_prefix: str,
247
+ ) -> dict[str, Any]:
248
+ """Consume config values for a nested flow section."""
249
+ nested_schema = field.get("schema")
250
+ if not isinstance(nested_schema, list):
251
+ return {}
252
+
253
+ name = field.get("name")
254
+ section_path = _section_path(path_prefix, name)
255
+ nested_data = _required_section_defaults(field)
256
+
257
+ if explicit_section is not None:
258
+ explicit_remaining = dict(explicit_section)
259
+ nested_data.update(
260
+ _consume_form_schema(
261
+ nested_schema,
262
+ explicit_remaining,
263
+ ignored_config_keys,
264
+ consumed_config_keys,
265
+ section_path,
266
+ )
267
+ )
268
+ _record_ignored_section_keys(
269
+ ignored_config_keys,
270
+ explicit_remaining,
271
+ section_path,
272
+ )
273
+
274
+ nested_data.update(
275
+ _consume_form_schema(
276
+ nested_schema,
277
+ remaining_config,
278
+ ignored_config_keys,
279
+ consumed_config_keys,
280
+ section_path,
281
+ )
282
+ )
283
+ return nested_data
284
+
285
+
286
+ def _mark_consumed(
287
+ consumed_config_keys: set[str] | None,
288
+ path_prefix: str,
289
+ name: str,
290
+ ) -> None:
291
+ """Record that a caller-supplied form key, not an injected default, was used."""
292
+ if consumed_config_keys is not None:
293
+ consumed_config_keys.add(_section_path(path_prefix, name))
294
+
295
+
296
+ def _auto_confirm_form_payload(current_step: dict[str, Any]) -> dict[str, Any] | None:
297
+ """Return payload for HA preview/confirmation-only forms we can safely advance."""
298
+ if "preview" not in current_step:
299
+ return None
300
+ data_schema = current_step.get("data_schema")
301
+ if not isinstance(data_schema, list) or len(data_schema) != 1:
302
+ return None
303
+ field = data_schema[0]
304
+ if not isinstance(field, dict) or not field.get("required"):
305
+ return None
306
+ name = field.get("name")
307
+ if not isinstance(name, str) or name in _MENU_SELECTION_KEYS:
308
+ return None
309
+ default = _field_default_value(field)
310
+ if default is not False:
311
+ return None
312
+ selector = field.get("selector")
313
+ if isinstance(selector, dict) and selector and "boolean" not in selector:
314
+ return None
315
+ return {name: True}
316
+
317
+
197
318
  def _consume_form_schema(
198
319
  data_schema: list[Any],
199
320
  remaining_config: dict[str, Any],
200
321
  ignored_config_keys: set[str] | None = None,
322
+ consumed_config_keys: set[str] | None = None,
201
323
  path_prefix: str = "",
202
324
  ) -> dict[str, Any]:
203
325
  """Consume matching config values and shape nested flow sections.
@@ -208,7 +330,6 @@ def _consume_form_schema(
208
330
  ``ignored_config_keys`` with their dotted section path.
209
331
  """
210
332
  form_data: dict[str, Any] = {}
211
- missing = object()
212
333
 
213
334
  for field in data_schema:
214
335
  if not isinstance(field, dict):
@@ -217,47 +338,27 @@ def _consume_form_schema(
217
338
  name = field.get("name")
218
339
  nested_schema = field.get("schema")
219
340
  if isinstance(nested_schema, list):
220
- section_path = _section_path(path_prefix, name)
221
- explicit_section = (
222
- remaining_config.pop(name, missing)
223
- if isinstance(name, str)
224
- else missing
225
- )
226
- if explicit_section is not missing and not isinstance(
227
- explicit_section, dict
228
- ):
229
- if isinstance(name, str):
230
- form_data[name] = explicit_section
231
- continue
232
-
233
- nested_data: dict[str, Any] = {}
234
- if isinstance(explicit_section, dict):
235
- explicit_remaining = dict(explicit_section)
236
- nested_data.update(
237
- _consume_form_schema(
238
- nested_schema,
239
- explicit_remaining,
240
- ignored_config_keys,
241
- section_path,
242
- )
243
- )
244
- _record_ignored_section_keys(
245
- ignored_config_keys,
246
- explicit_remaining,
247
- section_path,
248
- )
249
- nested_data.update(
250
- _consume_form_schema(
251
- nested_schema,
252
- remaining_config,
253
- ignored_config_keys,
254
- section_path,
255
- )
341
+ section_name = name if isinstance(name, str) else None
342
+ explicit_section = None
343
+ if section_name is not None and section_name in remaining_config:
344
+ explicit_value = remaining_config.pop(section_name)
345
+ if not isinstance(explicit_value, dict):
346
+ form_data[section_name] = explicit_value
347
+ _mark_consumed(consumed_config_keys, path_prefix, section_name)
348
+ continue
349
+ explicit_section = explicit_value
350
+
351
+ nested_data = _consume_section_schema(
352
+ field,
353
+ explicit_section,
354
+ remaining_config,
355
+ ignored_config_keys,
356
+ consumed_config_keys,
357
+ path_prefix,
256
358
  )
257
-
258
359
  if nested_data:
259
- if isinstance(name, str):
260
- form_data[name] = nested_data
360
+ if section_name is not None:
361
+ form_data[section_name] = nested_data
261
362
  else:
262
363
  form_data.update(nested_data)
263
364
  continue
@@ -268,6 +369,7 @@ def _consume_form_schema(
268
369
  and name in remaining_config
269
370
  ):
270
371
  form_data[name] = remaining_config.pop(name)
372
+ _mark_consumed(consumed_config_keys, path_prefix, name)
271
373
 
272
374
  return form_data
273
375
 
@@ -296,6 +398,7 @@ def _handle_form_step(
296
398
  current_step: dict[str, Any],
297
399
  remaining_config: dict[str, Any],
298
400
  ignored_config_keys: set[str] | None = None,
401
+ consumed_config_keys: set[str] | None = None,
299
402
  ) -> dict[str, Any]:
300
403
  """Validate a form step and return form data to submit.
301
404
 
@@ -337,9 +440,10 @@ def _handle_form_step(
337
440
  if key in _MENU_SELECTION_KEYS:
338
441
  continue
339
442
  form_data[key] = remaining_config.pop(key)
443
+ _mark_consumed(consumed_config_keys, "", key)
340
444
  else:
341
445
  form_data = _consume_form_schema(
342
- data_schema, remaining_config, ignored_config_keys
446
+ data_schema, remaining_config, ignored_config_keys, consumed_config_keys
343
447
  )
344
448
 
345
449
  return form_data
@@ -783,13 +887,17 @@ async def _handle_flow_steps(
783
887
  # for subsequent steps (HA can present multi-step forms, e.g.
784
888
  # statistics: user step then pick-characteristic step).
785
889
  saw_form_step = True
786
- form_data = _handle_form_step(
787
- flow_id,
788
- current_step,
789
- remaining_config,
790
- ignored_config_keys,
791
- )
792
- if form_data:
890
+ consumed_form_keys: set[str] = set()
891
+ form_data = _auto_confirm_form_payload(current_step)
892
+ if form_data is None:
893
+ form_data = _handle_form_step(
894
+ flow_id,
895
+ current_step,
896
+ remaining_config,
897
+ ignored_config_keys,
898
+ consumed_form_keys,
899
+ )
900
+ if consumed_form_keys:
793
901
  any_form_key_consumed = True
794
902
  logger.debug(
795
903
  f"Flow step {step_num}: form submit "
@@ -34,7 +34,7 @@ from ..utils.usage_logger import (
34
34
  )
35
35
  from .component_api import get_component_caps
36
36
  from .helpers import log_tool_usage, register_tool_methods
37
- from .util_helpers import ANSI_ESCAPE_RE
37
+ from .util_helpers import ANSI_ESCAPE_RE, JSON_STRING_COERCION, project_fields
38
38
 
39
39
  logger = logging.getLogger(__name__)
40
40
 
@@ -676,10 +676,38 @@ class BugReportTools:
676
676
  ),
677
677
  ),
678
678
  ] = 10,
679
+ fields: Annotated[
680
+ str | list[str] | None,
681
+ JSON_STRING_COERCION,
682
+ Field(
683
+ default=None,
684
+ description=(
685
+ "Return only the specified top-level response keys — the "
686
+ "full response (both templates + logs + diagnostics, with "
687
+ "log content repeated across the raw keys and templates) "
688
+ "is very large. "
689
+ "None = full response. Typical for a runtime bug: "
690
+ "'runtime_bug_template,suggested_title,"
691
+ "runtime_bug_submit_url,duplicate_check_urls,"
692
+ "anonymization_guide,instructions'; for agent feedback "
693
+ "swap in agent_behavior_template and "
694
+ "agent_behavior_submit_url. The templates already embed "
695
+ "the relevant logs, so the raw log keys are only needed "
696
+ "for your own analysis. "
697
+ "Available keys: diagnostic_info, recent_logs, "
698
+ "startup_logs, addon_logs, core_error_log, log_count, "
699
+ "startup_log_count, formatted_report, "
700
+ "runtime_bug_template, agent_behavior_template, "
701
+ "anonymization_guide, suggested_title, "
702
+ "runtime_bug_submit_url, agent_behavior_submit_url, "
703
+ "duplicate_check_urls, missing_tool_hint, instructions."
704
+ ),
705
+ ),
706
+ ] = None,
679
707
  ctx: Context | None = None,
680
708
  ) -> dict[str, Any]:
681
709
  """
682
- Collect diagnostic information for filing issue reports or feedback.
710
+ Get diagnostic information and templates for filing issue reports or feedback.
683
711
 
684
712
  This tool generates templates for TWO types of reports:
685
713
  1. **Runtime Bug Report** - For ha-mcp errors, failures, unexpected behavior
@@ -709,7 +737,10 @@ class BugReportTools:
709
737
  - "That was inefficient"
710
738
 
711
739
  **OUTPUT:**
712
- Returns both templates plus diagnostic data. Key fields:
740
+ Returns both templates plus diagnostic data. The full response is
741
+ LARGE (the captured logs appear in the raw log keys AND inside each
742
+ template) — pass fields=... to fetch only the keys you need once you
743
+ know which template applies. Key fields:
713
744
  - `runtime_bug_template`, `agent_behavior_template` — pick based on context
714
745
  - `recent_logs`, `startup_logs` — captured ha-mcp tool/server log entries
715
746
  - `addon_logs` — addon container stdout/stderr (HA add-on installs only;
@@ -843,7 +874,7 @@ class BugReportTools:
843
874
  for keyword in search_keywords[:3] # Limit to top 3 keywords
844
875
  ]
845
876
 
846
- return {
877
+ result: dict[str, Any] = {
847
878
  "success": True,
848
879
  "diagnostic_info": diagnostic_info,
849
880
  "recent_logs": recent_logs,
@@ -934,6 +965,7 @@ class BugReportTools:
934
965
  "CRITICAL: Always ANONYMIZE the report BEFORE presenting it in markdown code blocks!"
935
966
  ),
936
967
  }
968
+ return project_fields(result, fields)
937
969
 
938
970
 
939
971
  def register_bug_report_tools(mcp: Any, client: Any, **kwargs: Any) -> None: