ha-mcp-dev 7.12.3.dev1879__tar.gz → 7.12.3.dev1882__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 (164) hide show
  1. {ha_mcp_dev-7.12.3.dev1879/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.12.3.dev1882}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/value_sources.py +17 -1
  4. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/config_entry_flow.py +177 -38
  5. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_integrations.py +202 -48
  6. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  7. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/LICENSE +0 -0
  8. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/MANIFEST.in +0 -0
  9. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/README.md +0 -0
  10. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/setup.cfg +0 -0
  11. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/__init__.py +0 -0
  12. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/__main__.py +0 -0
  13. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/_pypi_marker +0 -0
  14. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/_version.py +0 -0
  15. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/auth/__init__.py +0 -0
  16. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/auth/consent_form.py +0 -0
  17. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/auth/provider.py +0 -0
  18. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/backup_manager.py +0 -0
  19. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/browser_landing.py +0 -0
  20. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/client/__init__.py +0 -0
  21. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/client/rest_client.py +0 -0
  22. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/client/supervisor_client.py +0 -0
  23. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/client/websocket_client.py +0 -0
  24. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/client/websocket_listener.py +0 -0
  25. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/config.py +0 -0
  26. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  27. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  28. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  29. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/errors.py +0 -0
  30. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/llm_exposure.py +0 -0
  31. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/__init__.py +0 -0
  32. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/approval_queue.py +0 -0
  33. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/evaluator.py +0 -0
  34. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/handlers.py +0 -0
  35. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/middleware.py +0 -0
  36. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/model.py +0 -0
  37. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/policy/persistence.py +0 -0
  38. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/py.typed +0 -0
  39. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/read_only.py +0 -0
  40. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  41. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  42. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  43. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  44. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  45. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  46. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  47. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  48. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  49. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  50. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  51. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  52. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  53. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  54. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  55. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  56. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  57. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  58. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  59. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  60. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  61. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  62. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  63. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  64. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  65. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/server.py +0 -0
  66. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/settings_ui/__init__.py +0 -0
  67. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/settings_ui/settings.css +0 -0
  68. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/settings_ui/settings.html +0 -0
  69. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/settings_ui/settings.js +0 -0
  70. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/smoke_test.py +0 -0
  71. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  72. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/__init__.py +0 -0
  73. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/auto_backup.py +0 -0
  74. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/backup.py +0 -0
  75. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  76. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/component_api.py +0 -0
  77. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/device_control.py +0 -0
  78. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/enhanced.py +0 -0
  79. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/helpers.py +0 -0
  80. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/radio/__init__.py +0 -0
  81. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/radio/base.py +0 -0
  82. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/radio/matter.py +0 -0
  83. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/radio/thread.py +0 -0
  84. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  85. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/radio/zwave.py +0 -0
  86. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/reference_validator.py +0 -0
  87. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/registry.py +0 -0
  88. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  89. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  90. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  91. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  92. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  93. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  94. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  95. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  96. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  97. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  98. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_addons.py +0 -0
  99. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_areas.py +0 -0
  100. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  101. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  102. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_calendar.py +0 -0
  103. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_camera.py +0 -0
  104. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_categories.py +0 -0
  105. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_code.py +0 -0
  106. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  107. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  108. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  109. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  110. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  111. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  112. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_dev.py +0 -0
  113. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_energy.py +0 -0
  114. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_entities.py +0 -0
  115. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  116. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_groups.py +0 -0
  117. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_hacs.py +0 -0
  118. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_history.py +0 -0
  119. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_labels.py +0 -0
  120. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  121. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_radio.py +0 -0
  122. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_registry.py +0 -0
  123. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_resources.py +0 -0
  124. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_search.py +0 -0
  125. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_service.py +0 -0
  126. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_services.py +0 -0
  127. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_system.py +0 -0
  128. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_themes.py +0 -0
  129. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_todo.py +0 -0
  130. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_traces.py +0 -0
  131. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_updates.py +0 -0
  132. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_utility.py +0 -0
  133. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  134. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  135. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/tools_zones.py +0 -0
  136. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/util_helpers.py +0 -0
  137. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/tools/validation_middleware.py +0 -0
  138. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/transforms/__init__.py +0 -0
  139. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/transforms/categorized_search.py +0 -0
  140. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  141. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/transport_security.py +0 -0
  142. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/update_check.py +0 -0
  143. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/__init__.py +0 -0
  144. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/config_hash.py +0 -0
  145. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/data_paths.py +0 -0
  146. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/domain_handlers.py +0 -0
  147. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  148. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  149. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/operation_manager.py +0 -0
  150. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/python_sandbox.py +0 -0
  151. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/skill_loader.py +0 -0
  152. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/utils/usage_logger.py +0 -0
  153. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/visibility/__init__.py +0 -0
  154. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/visibility/model.py +0 -0
  155. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/visibility/persistence.py +0 -0
  156. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp/visibility/resolver.py +0 -0
  157. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  158. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  159. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  160. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  161. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  162. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/tests/__init__.py +0 -0
  163. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/tests/test_constants.py +0 -0
  164. {ha_mcp_dev-7.12.3.dev1879 → ha_mcp_dev-7.12.3.dev1882}/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.12.3.dev1879
3
+ Version: 7.12.3.dev1882
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.12.3.dev1879"
7
+ version = "7.12.3.dev1882"
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
@@ -40,7 +40,7 @@ VALUE_SOURCE_REGISTRY: dict[tuple[str, str], str] = {
40
40
  ("ha_get_history", "args.entity_ids"): "ha_entities",
41
41
  ("ha_remove_entity", "args.entity_id"): "ha_entities",
42
42
  ("ha_get_entity_exposure", "args.entity_id"): "ha_entities",
43
- ("ha_set_integration_enabled", "args.entity_id"): "ha_entities",
43
+ ("ha_set_integration", "args.entry_id"): "ha_config_entries",
44
44
  }
45
45
 
46
46
  # Fetched-value TTL cache so the UI can click through path options
@@ -171,8 +171,24 @@ async def _fetch_ha_entities(client: Any, params: dict[str, str]) -> list[str]:
171
171
  return out
172
172
 
173
173
 
174
+ async def _fetch_ha_config_entries(client: Any, _params: dict[str, str]) -> list[str]:
175
+ entries = await client._request("GET", "/config/config_entries/entry")
176
+ if not isinstance(entries, list):
177
+ logger.warning(
178
+ "ha_config_entries: config entry list returned unexpected shape %s",
179
+ type(entries).__name__,
180
+ )
181
+ return []
182
+ return [
183
+ e["entry_id"]
184
+ for e in entries
185
+ if isinstance(e, dict) and isinstance(e.get("entry_id"), str)
186
+ ]
187
+
188
+
174
189
  _FETCHERS: dict[str, Callable[[Any, dict[str, str]], Awaitable[list[str]]]] = {
175
190
  "ha_domains": _fetch_ha_domains,
176
191
  "ha_services": _fetch_ha_services,
177
192
  "ha_entities": _fetch_ha_entities,
193
+ "ha_config_entries": _fetch_ha_config_entries,
178
194
  }
@@ -14,7 +14,7 @@ import asyncio
14
14
  import logging
15
15
  from collections.abc import Iterator
16
16
  from enum import StrEnum
17
- from typing import Any, Literal
17
+ from typing import Any, Literal, NoReturn
18
18
 
19
19
  from ..client.rest_client import HomeAssistantAPIError
20
20
  from ..errors import ErrorCode, create_error_response
@@ -65,6 +65,13 @@ FLOW_HELPER_TYPES: frozenset[str] = frozenset(
65
65
 
66
66
  # Keys used to specify a menu selection — stripped before submitting form data.
67
67
  _MENU_SELECTION_KEYS = frozenset({"group_type", "next_step_id", "menu_option"})
68
+
69
+ # Flow step types an MCP client cannot drive: external steps need a browser
70
+ # (OAuth / cloud authorization), progress steps wait on HA-side async work.
71
+ # Surfaced as structured errors instead of attempted (issue #1814).
72
+ _UNDRIVABLE_STEP_TYPES = frozenset(
73
+ {"external", "external_done", "progress", "progress_done"}
74
+ )
68
75
  _RECONFIGURE_SUCCESS_REASONS = frozenset(
69
76
  {
70
77
  "reauth_successful",
@@ -618,6 +625,74 @@ async def _submit_step(
618
625
  raise
619
626
 
620
627
 
628
+ def _finish_flow_entry(
629
+ flow_id: str,
630
+ current_step: dict[str, Any],
631
+ *,
632
+ supplied_keys: list[str],
633
+ saw_form_step: bool,
634
+ any_form_key_consumed: bool,
635
+ ignored_config_keys: set[str],
636
+ remaining_config: dict[str, Any],
637
+ ) -> dict[str, Any]:
638
+ """Build the CREATE_ENTRY success response, or raise on total key miss.
639
+
640
+ When the flow presented at least one form step, the caller supplied
641
+ config keys, and NONE were consumed by any form (typos / wrong field
642
+ names), the flow was walked on empty forms and HA saved form defaults —
643
+ not the caller's values. A success + warning there reads as "done" to an
644
+ LLM caller, so fail loudly with the schema route instead. Partial
645
+ consumption — and flows that complete without any form step (instant
646
+ creates) — keep the established success + warnings contract.
647
+ """
648
+ if supplied_keys and saw_form_step and not any_form_key_consumed:
649
+ raise_tool_error(
650
+ create_error_response(
651
+ ErrorCode.VALIDATION_INVALID_PARAMETER,
652
+ "Flow completed without consuming any of the supplied "
653
+ "config keys — every form step was submitted empty, so "
654
+ "the flow saved its defaults, not your values",
655
+ suggestions=[
656
+ "Check the field names against the flow's data_schema — "
657
+ "ha_get_integration(entry_id=..., include_schema=True) "
658
+ "shows the accepted fields — then retry with corrected "
659
+ "keys.",
660
+ ],
661
+ context={
662
+ "flow_id": flow_id,
663
+ "supplied_keys": supplied_keys,
664
+ "details": current_step,
665
+ },
666
+ )
667
+ )
668
+ response: dict[str, Any] = {"success": True, "entry": current_step}
669
+ warnings = _ignored_keys_warnings(ignored_config_keys, remaining_config)
670
+ if warnings:
671
+ response["warnings"] = warnings
672
+ return response
673
+
674
+
675
+ def _raise_flow_abort(flow_id: str, current_step: dict[str, Any]) -> NoReturn:
676
+ """Raise the structured error for an ABORT flow step."""
677
+ reason = current_step.get("reason")
678
+ abort_suggestions: list[str] = []
679
+ if reason in ("already_configured", "single_instance_allowed"):
680
+ # Common benign aborts on the add-integration path (#1814): give the
681
+ # caller a route to the existing entry instead of a bare failure.
682
+ abort_suggestions.append(
683
+ "The integration is already set up — use "
684
+ "ha_get_integration() to find the existing entry."
685
+ )
686
+ raise_tool_error(
687
+ create_error_response(
688
+ ErrorCode.SERVICE_CALL_FAILED,
689
+ f"Flow aborted: {reason}",
690
+ suggestions=abort_suggestions or None,
691
+ context={"flow_id": flow_id, "details": current_step},
692
+ )
693
+ )
694
+
695
+
621
696
  async def _handle_flow_steps(
622
697
  client: Any,
623
698
  flow_id: str,
@@ -650,8 +725,12 @@ async def _handle_flow_steps(
650
725
 
651
726
  Returns:
652
727
  ``{"success": True, "entry": result}`` on success, plus ``warnings``
653
- when caller-supplied config keys were not declared by any flow step.
654
- Raises ToolError on any failure.
728
+ when SOME caller-supplied config keys were not declared by any flow
729
+ step. When the flow presented at least one form step and NONE of the
730
+ supplied keys were consumed, raises ``VALIDATION_INVALID_PARAMETER``
731
+ instead of reporting a misleading success — the flow completed on
732
+ empty forms (defaults), applying nothing the caller asked for (see
733
+ :func:`_finish_flow_entry`). Raises ToolError on any failure.
655
734
  """
656
735
  if submit_fn is None:
657
736
  submit_fn = client.submit_config_flow_step
@@ -659,26 +738,27 @@ async def _handle_flow_steps(
659
738
  current_step = initial_step
660
739
  last_menu_choice: str | None = None
661
740
  ignored_config_keys: set[str] = set()
741
+ supplied_keys = sorted(k for k in config if k not in _MENU_SELECTION_KEYS)
742
+ saw_form_step = False
743
+ any_form_key_consumed = False
662
744
  max_steps = 10
663
745
 
664
746
  for step_num in range(max_steps):
665
747
  result_type = current_step.get("type")
666
748
 
667
749
  if result_type == _FlowType.CREATE_ENTRY:
668
- response: dict[str, Any] = {"success": True, "entry": current_step}
669
- warnings = _ignored_keys_warnings(ignored_config_keys, remaining_config)
670
- if warnings:
671
- response["warnings"] = warnings
672
- return response
750
+ return _finish_flow_entry(
751
+ flow_id,
752
+ current_step,
753
+ supplied_keys=supplied_keys,
754
+ saw_form_step=saw_form_step,
755
+ any_form_key_consumed=any_form_key_consumed,
756
+ ignored_config_keys=ignored_config_keys,
757
+ remaining_config=remaining_config,
758
+ )
673
759
 
674
760
  if result_type == _FlowType.ABORT:
675
- raise_tool_error(
676
- create_error_response(
677
- ErrorCode.SERVICE_CALL_FAILED,
678
- f"Flow aborted: {current_step.get('reason')}",
679
- context={"flow_id": flow_id, "details": current_step},
680
- )
681
- )
761
+ _raise_flow_abort(flow_id, current_step)
682
762
 
683
763
  if result_type == _FlowType.MENU:
684
764
  menu_choice = _handle_menu_step(flow_id, current_step, remaining_config)
@@ -702,12 +782,15 @@ async def _handle_flow_steps(
702
782
  # step's data_schema, leaving any other keys in remaining_config
703
783
  # for subsequent steps (HA can present multi-step forms, e.g.
704
784
  # statistics: user step then pick-characteristic step).
785
+ saw_form_step = True
705
786
  form_data = _handle_form_step(
706
787
  flow_id,
707
788
  current_step,
708
789
  remaining_config,
709
790
  ignored_config_keys,
710
791
  )
792
+ if form_data:
793
+ any_form_key_consumed = True
711
794
  logger.debug(
712
795
  f"Flow step {step_num}: form submit "
713
796
  f"(step_id={current_step.get('step_id')}, keys={list(form_data.keys())})"
@@ -722,6 +805,21 @@ async def _handle_flow_steps(
722
805
  current_step=current_step,
723
806
  )
724
807
 
808
+ elif result_type in _UNDRIVABLE_STEP_TYPES:
809
+ raise_tool_error(
810
+ create_error_response(
811
+ ErrorCode.SERVICE_CALL_FAILED,
812
+ f"Flow reached a '{result_type}' step that cannot be "
813
+ "completed via MCP (browser/OAuth authorization or an "
814
+ "asynchronous provider step)",
815
+ suggestions=[
816
+ "Complete this flow in the Home Assistant UI "
817
+ "(Settings > Devices & Services)."
818
+ ],
819
+ context={"flow_id": flow_id, "details": current_step},
820
+ )
821
+ )
822
+
725
823
  else:
726
824
  raise_tool_error(
727
825
  create_error_response(
@@ -981,30 +1079,35 @@ async def get_user_step_field_names(client: Any, helper_type: str) -> set[str] |
981
1079
  )
982
1080
 
983
1081
 
984
- async def update_flow_helper(
1082
+ async def update_config_entry_options(
985
1083
  client: Any,
986
- helper_type: str,
987
- config_dict: dict[str, Any],
988
1084
  entry_id: str,
1085
+ config_dict: dict[str, Any],
1086
+ *,
1087
+ expected_domain: str | None = None,
1088
+ noun: str = "integration",
989
1089
  ) -> dict[str, Any]:
990
- """Update an existing flow-based helper via its options flow.
1090
+ """Update an existing config entry via its options flow.
991
1091
 
992
- Verifies the entry domain matches helper_type, starts an options flow,
993
- walks the flow steps, and returns the result. Aborts the flow on error.
1092
+ When ``expected_domain`` is provided, verifies the entry's domain matches
1093
+ it first (the helper path passes the helper_type; the generic
1094
+ ``ha_set_integration`` path passes ``None`` to accept any domain). Starts
1095
+ an options flow, walks the flow steps, and returns the result. Aborts the
1096
+ flow on error. ``noun`` only affects response wording.
994
1097
  """
995
1098
  config_entry = await client.get_config_entry(entry_id)
996
1099
  actual_domain = config_entry.get("domain")
997
- if actual_domain != helper_type:
1100
+ if expected_domain is not None and actual_domain != expected_domain:
998
1101
  raise_tool_error(
999
1102
  create_error_response(
1000
1103
  ErrorCode.VALIDATION_INVALID_PARAMETER,
1001
- f"entry_id '{entry_id}' belongs to domain '{actual_domain}', not '{helper_type}'",
1104
+ f"entry_id '{entry_id}' belongs to domain '{actual_domain}', not '{expected_domain}'",
1002
1105
  suggestions=[
1003
- f"Use ha_get_integration(domain='{helper_type}') to find valid entry IDs",
1106
+ f"Use ha_get_integration(domain='{expected_domain}') to find valid entry IDs",
1004
1107
  ],
1005
1108
  context={
1006
1109
  "entry_id": entry_id,
1007
- "expected": helper_type,
1110
+ "expected": expected_domain,
1008
1111
  "actual": actual_domain,
1009
1112
  },
1010
1113
  )
@@ -1032,7 +1135,7 @@ async def update_flow_helper(
1032
1135
  flow_result,
1033
1136
  config_dict,
1034
1137
  submit_fn=client.submit_options_flow_step,
1035
- helper_type=helper_type,
1138
+ helper_type=expected_domain,
1036
1139
  )
1037
1140
  except Exception:
1038
1141
  try:
@@ -1048,8 +1151,8 @@ async def update_flow_helper(
1048
1151
  "success": True,
1049
1152
  "entry_id": entry_id,
1050
1153
  "title": entry.get("title"),
1051
- "domain": helper_type,
1052
- "message": f"{helper_type} helper updated successfully",
1154
+ "domain": actual_domain,
1155
+ "message": f"{actual_domain} {noun} updated successfully",
1053
1156
  "updated": True,
1054
1157
  }
1055
1158
  if result.get("warnings"):
@@ -1057,17 +1160,40 @@ async def update_flow_helper(
1057
1160
  return response
1058
1161
 
1059
1162
 
1060
- async def create_flow_helper(
1163
+ async def update_flow_helper(
1061
1164
  client: Any,
1062
1165
  helper_type: str,
1063
1166
  config_dict: dict[str, Any],
1167
+ entry_id: str,
1064
1168
  ) -> dict[str, Any]:
1065
- """Create a new flow-based helper via the config flow.
1169
+ """Update an existing flow-based helper via its options flow.
1066
1170
 
1067
- Starts a config flow, walks the flow steps, and returns the result.
1068
- Aborts the flow on error.
1171
+ Verifies the entry domain matches helper_type, starts an options flow,
1172
+ walks the flow steps, and returns the result. Aborts the flow on error.
1069
1173
  """
1070
- flow_result = await client.start_config_flow(helper_type)
1174
+ return await update_config_entry_options(
1175
+ client,
1176
+ entry_id,
1177
+ config_dict,
1178
+ expected_domain=helper_type,
1179
+ noun="helper",
1180
+ )
1181
+
1182
+
1183
+ async def create_config_entry(
1184
+ client: Any,
1185
+ domain: str,
1186
+ config_dict: dict[str, Any],
1187
+ *,
1188
+ noun: str = "integration",
1189
+ ) -> dict[str, Any]:
1190
+ """Create a config entry by driving ``domain``'s config flow.
1191
+
1192
+ Starts a config flow, walks the flow steps (menus and multi-step forms),
1193
+ and returns the result. Aborts the flow on error. ``noun`` only affects
1194
+ response wording.
1195
+ """
1196
+ flow_result = await client.start_config_flow(domain)
1071
1197
  flow_id = flow_result.get("flow_id")
1072
1198
 
1073
1199
  if not flow_id:
@@ -1076,9 +1202,9 @@ async def create_flow_helper(
1076
1202
  ErrorCode.SERVICE_CALL_FAILED,
1077
1203
  "Failed to start config flow",
1078
1204
  suggestions=[
1079
- "Check that the helper type is supported and Home Assistant is reachable"
1205
+ f"Check that the {noun} domain exists and Home Assistant is reachable"
1080
1206
  ],
1081
- context={"helper_type": helper_type, "details": flow_result},
1207
+ context={"domain": domain, "details": flow_result},
1082
1208
  )
1083
1209
  )
1084
1210
 
@@ -1088,7 +1214,7 @@ async def create_flow_helper(
1088
1214
  flow_id,
1089
1215
  flow_result,
1090
1216
  config_dict,
1091
- helper_type=helper_type,
1217
+ helper_type=domain,
1092
1218
  )
1093
1219
  except Exception:
1094
1220
  try:
@@ -1104,9 +1230,22 @@ async def create_flow_helper(
1104
1230
  "success": True,
1105
1231
  "entry_id": entry.get("entry_id"),
1106
1232
  "title": entry.get("title"),
1107
- "domain": helper_type,
1108
- "message": f"{helper_type} helper created successfully",
1233
+ "domain": domain,
1234
+ "message": f"{domain} {noun} created successfully",
1109
1235
  }
1110
1236
  if result.get("warnings"):
1111
1237
  response["warnings"] = result["warnings"]
1112
1238
  return response
1239
+
1240
+
1241
+ async def create_flow_helper(
1242
+ client: Any,
1243
+ helper_type: str,
1244
+ config_dict: dict[str, Any],
1245
+ ) -> dict[str, Any]:
1246
+ """Create a new flow-based helper via the config flow.
1247
+
1248
+ Starts a config flow, walks the flow steps, and returns the result.
1249
+ Aborts the flow on error.
1250
+ """
1251
+ return await create_config_entry(client, helper_type, config_dict, noun="helper")