ha-mcp-dev 7.9.0.dev761__tar.gz → 7.9.0.dev763__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 (154) hide show
  1. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/PKG-INFO +33 -4
  2. ha_mcp_dev-7.9.0.dev761/src/ha_mcp_dev.egg-info/PKG-INFO → ha_mcp_dev-7.9.0.dev763/README.md +29 -33
  3. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/pyproject.toml +21 -3
  4. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/_version.py +21 -1
  5. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/config.py +96 -5
  6. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/settings.css +76 -12
  7. ha_mcp_dev-7.9.0.dev761/README.md → ha_mcp_dev-7.9.0.dev763/src/ha_mcp_dev.egg-info/PKG-INFO +62 -1
  8. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/requires.txt +1 -1
  9. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/LICENSE +0 -0
  10. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/MANIFEST.in +0 -0
  11. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/setup.cfg +0 -0
  12. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/__init__.py +0 -0
  13. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/__main__.py +0 -0
  14. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/_pypi_marker +0 -0
  15. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/auth/__init__.py +0 -0
  16. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/auth/consent_form.py +0 -0
  17. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/auth/provider.py +0 -0
  18. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/backup_manager.py +0 -0
  19. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/__init__.py +0 -0
  20. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/rest_client.py +0 -0
  21. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/supervisor_client.py +0 -0
  22. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/websocket_client.py +0 -0
  23. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/client/websocket_listener.py +0 -0
  24. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  25. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  26. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  27. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/errors.py +0 -0
  28. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/__init__.py +0 -0
  29. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/approval_queue.py +0 -0
  30. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/evaluator.py +0 -0
  31. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/handlers.py +0 -0
  32. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/middleware.py +0 -0
  33. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/model.py +0 -0
  34. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/persistence.py +0 -0
  35. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/policy/value_sources.py +0 -0
  36. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/py.typed +0 -0
  37. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/read_only.py +0 -0
  38. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  39. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  40. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  41. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  42. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  43. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  44. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  45. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  46. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  47. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  48. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  49. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  50. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  51. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  52. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  53. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  54. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  55. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  56. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  57. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  58. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  59. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  60. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  61. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  62. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/server.py +0 -0
  63. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/__init__.py +0 -0
  64. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/settings.html +0 -0
  65. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/settings_ui/settings.js +0 -0
  66. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/smoke_test.py +0 -0
  67. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  68. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/__init__.py +0 -0
  69. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/auto_backup.py +0 -0
  70. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/backup.py +0 -0
  71. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  72. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  73. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/device_control.py +0 -0
  74. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/enhanced.py +0 -0
  75. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/helpers.py +0 -0
  76. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/__init__.py +0 -0
  77. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/base.py +0 -0
  78. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/matter.py +0 -0
  79. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/thread.py +0 -0
  80. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  81. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/radio/zwave.py +0 -0
  82. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/reference_validator.py +0 -0
  83. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/registry.py +0 -0
  84. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  85. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  86. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  87. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  88. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  89. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  90. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  91. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  92. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  93. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  94. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_addons.py +0 -0
  95. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_areas.py +0 -0
  96. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  97. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  98. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_calendar.py +0 -0
  99. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_camera.py +0 -0
  100. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_categories.py +0 -0
  101. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_code.py +0 -0
  102. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  103. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  104. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  105. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  106. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  107. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  108. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_energy.py +0 -0
  109. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_entities.py +0 -0
  110. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  111. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_groups.py +0 -0
  112. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_hacs.py +0 -0
  113. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_history.py +0 -0
  114. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_integrations.py +0 -0
  115. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_labels.py +0 -0
  116. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  117. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_radio.py +0 -0
  118. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_registry.py +0 -0
  119. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_resources.py +0 -0
  120. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_search.py +0 -0
  121. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_service.py +0 -0
  122. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_services.py +0 -0
  123. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_system.py +0 -0
  124. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_themes.py +0 -0
  125. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_todo.py +0 -0
  126. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_traces.py +0 -0
  127. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_updates.py +0 -0
  128. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_utility.py +0 -0
  129. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  130. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  131. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/tools_zones.py +0 -0
  132. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/util_helpers.py +0 -0
  133. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/tools/validation_middleware.py +0 -0
  134. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/transforms/__init__.py +0 -0
  135. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/transforms/categorized_search.py +0 -0
  136. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  137. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/update_check.py +0 -0
  138. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/__init__.py +0 -0
  139. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/config_hash.py +0 -0
  140. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/data_paths.py +0 -0
  141. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/domain_handlers.py +0 -0
  142. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  143. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  144. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/operation_manager.py +0 -0
  145. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/python_sandbox.py +0 -0
  146. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/skill_loader.py +0 -0
  147. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp/utils/usage_logger.py +0 -0
  148. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  149. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  150. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  151. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  152. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/tests/__init__.py +0 -0
  153. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/tests/test_constants.py +0 -0
  154. {ha_mcp_dev-7.9.0.dev761 → ha_mcp_dev-7.9.0.dev763}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.9.0.dev761
3
+ Version: 7.9.0.dev763
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
@@ -13,9 +13,10 @@ Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
16
17
  Classifier: Topic :: Home Automation
17
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Requires-Python: <3.14,>=3.13
19
+ Requires-Python: <3.15,>=3.13
19
20
  Description-Content-Type: text/markdown
20
21
  License-File: LICENSE
21
22
  Requires-Dist: fastmcp==3.4.2
@@ -24,7 +25,7 @@ Requires-Dist: pydantic==2.13.4
24
25
  Requires-Dist: python-dotenv==1.2.2
25
26
  Requires-Dist: truststore==0.10.4
26
27
  Requires-Dist: websockets==16.0
27
- Requires-Dist: cryptography==49.0.0
28
+ Requires-Dist: cryptography<50,>=48.0.0
28
29
  Requires-Dist: pydantic-monty==0.0.18
29
30
  Requires-Dist: tzdata>=2024.1
30
31
  Requires-Dist: packaging>=24.0
@@ -92,6 +93,21 @@ Running Home Assistant OS? Run ha-mcp **inside** Home Assistant — no access to
92
93
 
93
94
  > ⚠️ **Two ways to run ha-mcp — don't mix them.** The add-on above is the recommended path. The local **stdio** path below runs ha-mcp on your own computer; it is more advanced, has fewer features, and is behind most connection problems. If you use the add-on, your Claude Desktop config points at the add-on URL through `mcp-proxy` with **no token** — do **not** also keep a local `uvx ha-mcp@latest` entry (with `HOMEASSISTANT_URL` / `HOMEASSISTANT_TOKEN`) in your Claude Desktop config. Running both at once is a known cause of connection hangs.
94
95
 
96
+ <details>
97
+ <summary><b>Run inside Home Assistant (Container / Core — no add-on)</b></summary>
98
+
99
+ Not on Home Assistant OS? Home Assistant **Container** and **Core** installs can't run add-ons, but the **HA-MCP Custom Component** (`ha_mcp_tools`) can run the **full ha-mcp server in-process**, inside Home Assistant — no separate Docker container, no token to manage. It works on Home Assistant OS too, and coexists with the add-on. The connect URL is a Home Assistant webhook, so it reaches remote MCP clients through **Nabu Casa** (or any reverse proxy) with no extra tunnel.
100
+
101
+ 1. Install **HA-MCP Custom Component** from HACS (the same repository you use for ha-mcp), or copy `custom_components/ha_mcp_tools` from this repository into your Home Assistant `config/custom_components/` directory; then restart Home Assistant
102
+ 2. Add the integration (**Settings → Devices & Services → Add Integration → HA-MCP Custom Component**) and choose **HA-MCP Server** from the menu, then submit — creating the entry starts the server
103
+ 3. Copy the connect URL from the **HA-MCP Server** notification (also shown on the entry's Configure screen)
104
+ 4. Manage the server from the **HA-MCP** sidebar panel (admin-only web settings UI)
105
+ 5. Connect your AI client to that URL
106
+
107
+ [Full in-process server documentation →](docs/in-process-server.md)
108
+
109
+ </details>
110
+
95
111
  ### 💻 Run locally with Claude Desktop (stdio · advanced)
96
112
 
97
113
  *No paid subscription required.* This runs ha-mcp on your own machine over stdio.
@@ -298,7 +314,7 @@ These tools also require feature flags: `HAMCP_ENABLE_FILESYSTEM_TOOLS=true` (fi
298
314
 
299
315
  To add manually: open **HACS** > **Integrations** > three-dot menu > **Custom repositories** > add `https://github.com/homeassistant-ai/ha-mcp` (category: Integration) > **Download**.
300
316
 
301
- After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **Home Assistant MCP Server Custom Component**.
317
+ After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **HA-MCP Custom Component**.
302
318
 
303
319
  On **Home Assistant OS / Supervised**, the integration offers to add the add-on repository and install and start the **Home Assistant MCP Server** add-on for you — no need to add the add-on repository by hand. On **Container / Core** installs (no Supervisor) there is no add-on; run the server via Docker or pip and the integration just sets up the file/YAML services.
304
320
 
@@ -306,6 +322,19 @@ On **Home Assistant OS / Supervised**, the integration offers to add the add-on
306
322
 
307
323
  Copy `custom_components/ha_mcp_tools/` from this repository into your HA `config/custom_components/` directory. Restart Home Assistant, then add the integration as described above.
308
324
 
325
+ ### Run the MCP server inside Home Assistant (in-process server entry)
326
+
327
+ The **HA-MCP Custom Component** offers a second config-entry type, **HA-MCP Server**, that runs the **full ha-mcp server in-process**, inside Home Assistant, and exposes it remotely through a Home Assistant webhook. This is a full install method in its own right — useful for **Home Assistant Container / Core** users who can't run add-ons, and available on Home Assistant OS too (it coexists with the add-on on a different default port, `9584` vs `9583`).
328
+
329
+ - **Install:** install **HA-MCP Custom Component** from HACS, or copy `custom_components/ha_mcp_tools` into your `config/custom_components/` directory and restart Home Assistant. Then **Add Integration → HA-MCP Custom Component → HA-MCP Server** and submit — creating the entry starts the server.
330
+ - **Connect URL:** appears in the **HA-MCP Server** notification and on the entry's Configure screen. It's a Home Assistant webhook — `https://<nabu-casa-domain>/api/webhook/<id>` remotely (through Nabu Casa or any reverse proxy) or `http://<home-assistant-host>:8123/api/webhook/<id>` locally. The server is also reachable directly on its own port by default (set **Network access** to `127.0.0.1` to turn direct access off).
331
+ - **Settings panel:** while the server runs, an admin-only **HA-MCP** panel in the Home Assistant sidebar opens its web settings UI (enable/disable/pin tools, feature flags, backups, themes) through Home Assistant itself — no loopback URL needed, the same idea as the add-on's "Open Web UI" button.
332
+ - **Authentication:** by default the secret webhook URL is the credential; optionally require Home Assistant account sign-in (`ha_auth`) for clients that support it.
333
+ - **Release channel:** the entry options let you pick `stable` (the pinned release) or `dev` (the latest development build, refreshed on every reload/restart).
334
+ - **The HA MCP Tools services entry is optional but recommended alongside it** — that second entry type of the same component provides the privileged file/YAML services the server's file tools use, exactly as with the add-on, Docker, and pip installs.
335
+
336
+ [Full in-process server documentation →](docs/in-process-server.md)
337
+
309
338
  ---
310
339
 
311
340
  ## 🧠 Better Results with Agent Skills
@@ -1,35 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: ha-mcp-dev
3
- Version: 7.9.0.dev761
4
- Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
- Author-email: Julien <github@qc-h.net>
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/homeassistant-ai/ha-mcp
8
- Project-URL: Bug Tracker, https://github.com/homeassistant-ai/ha-mcp/issues
9
- Project-URL: Repository, https://github.com/homeassistant-ai/ha-mcp
10
- Keywords: mcp,home-assistant,ai,automation,smart-home
11
- Classifier: Development Status :: 5 - Production/Stable
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Topic :: Home Automation
17
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
- Requires-Python: <3.14,>=3.13
19
- Description-Content-Type: text/markdown
20
- License-File: LICENSE
21
- Requires-Dist: fastmcp==3.4.2
22
- Requires-Dist: httpx[socks]==0.28.1
23
- Requires-Dist: pydantic==2.13.4
24
- Requires-Dist: python-dotenv==1.2.2
25
- Requires-Dist: truststore==0.10.4
26
- Requires-Dist: websockets==16.0
27
- Requires-Dist: cryptography==49.0.0
28
- Requires-Dist: pydantic-monty==0.0.18
29
- Requires-Dist: tzdata>=2024.1
30
- Requires-Dist: packaging>=24.0
31
- Dynamic: license-file
32
-
33
1
  > **Breaking change (v7.3.0):** `ha_config_set_yaml` has been moved to [beta](docs/beta.md).
34
2
 
35
3
  <div align="center">
@@ -92,6 +60,21 @@ Running Home Assistant OS? Run ha-mcp **inside** Home Assistant — no access to
92
60
 
93
61
  > ⚠️ **Two ways to run ha-mcp — don't mix them.** The add-on above is the recommended path. The local **stdio** path below runs ha-mcp on your own computer; it is more advanced, has fewer features, and is behind most connection problems. If you use the add-on, your Claude Desktop config points at the add-on URL through `mcp-proxy` with **no token** — do **not** also keep a local `uvx ha-mcp@latest` entry (with `HOMEASSISTANT_URL` / `HOMEASSISTANT_TOKEN`) in your Claude Desktop config. Running both at once is a known cause of connection hangs.
94
62
 
63
+ <details>
64
+ <summary><b>Run inside Home Assistant (Container / Core — no add-on)</b></summary>
65
+
66
+ Not on Home Assistant OS? Home Assistant **Container** and **Core** installs can't run add-ons, but the **HA-MCP Custom Component** (`ha_mcp_tools`) can run the **full ha-mcp server in-process**, inside Home Assistant — no separate Docker container, no token to manage. It works on Home Assistant OS too, and coexists with the add-on. The connect URL is a Home Assistant webhook, so it reaches remote MCP clients through **Nabu Casa** (or any reverse proxy) with no extra tunnel.
67
+
68
+ 1. Install **HA-MCP Custom Component** from HACS (the same repository you use for ha-mcp), or copy `custom_components/ha_mcp_tools` from this repository into your Home Assistant `config/custom_components/` directory; then restart Home Assistant
69
+ 2. Add the integration (**Settings → Devices & Services → Add Integration → HA-MCP Custom Component**) and choose **HA-MCP Server** from the menu, then submit — creating the entry starts the server
70
+ 3. Copy the connect URL from the **HA-MCP Server** notification (also shown on the entry's Configure screen)
71
+ 4. Manage the server from the **HA-MCP** sidebar panel (admin-only web settings UI)
72
+ 5. Connect your AI client to that URL
73
+
74
+ [Full in-process server documentation →](docs/in-process-server.md)
75
+
76
+ </details>
77
+
95
78
  ### 💻 Run locally with Claude Desktop (stdio · advanced)
96
79
 
97
80
  *No paid subscription required.* This runs ha-mcp on your own machine over stdio.
@@ -298,7 +281,7 @@ These tools also require feature flags: `HAMCP_ENABLE_FILESYSTEM_TOOLS=true` (fi
298
281
 
299
282
  To add manually: open **HACS** > **Integrations** > three-dot menu > **Custom repositories** > add `https://github.com/homeassistant-ai/ha-mcp` (category: Integration) > **Download**.
300
283
 
301
- After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **Home Assistant MCP Server Custom Component**.
284
+ After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **HA-MCP Custom Component**.
302
285
 
303
286
  On **Home Assistant OS / Supervised**, the integration offers to add the add-on repository and install and start the **Home Assistant MCP Server** add-on for you — no need to add the add-on repository by hand. On **Container / Core** installs (no Supervisor) there is no add-on; run the server via Docker or pip and the integration just sets up the file/YAML services.
304
287
 
@@ -306,6 +289,19 @@ On **Home Assistant OS / Supervised**, the integration offers to add the add-on
306
289
 
307
290
  Copy `custom_components/ha_mcp_tools/` from this repository into your HA `config/custom_components/` directory. Restart Home Assistant, then add the integration as described above.
308
291
 
292
+ ### Run the MCP server inside Home Assistant (in-process server entry)
293
+
294
+ The **HA-MCP Custom Component** offers a second config-entry type, **HA-MCP Server**, that runs the **full ha-mcp server in-process**, inside Home Assistant, and exposes it remotely through a Home Assistant webhook. This is a full install method in its own right — useful for **Home Assistant Container / Core** users who can't run add-ons, and available on Home Assistant OS too (it coexists with the add-on on a different default port, `9584` vs `9583`).
295
+
296
+ - **Install:** install **HA-MCP Custom Component** from HACS, or copy `custom_components/ha_mcp_tools` into your `config/custom_components/` directory and restart Home Assistant. Then **Add Integration → HA-MCP Custom Component → HA-MCP Server** and submit — creating the entry starts the server.
297
+ - **Connect URL:** appears in the **HA-MCP Server** notification and on the entry's Configure screen. It's a Home Assistant webhook — `https://<nabu-casa-domain>/api/webhook/<id>` remotely (through Nabu Casa or any reverse proxy) or `http://<home-assistant-host>:8123/api/webhook/<id>` locally. The server is also reachable directly on its own port by default (set **Network access** to `127.0.0.1` to turn direct access off).
298
+ - **Settings panel:** while the server runs, an admin-only **HA-MCP** panel in the Home Assistant sidebar opens its web settings UI (enable/disable/pin tools, feature flags, backups, themes) through Home Assistant itself — no loopback URL needed, the same idea as the add-on's "Open Web UI" button.
299
+ - **Authentication:** by default the secret webhook URL is the credential; optionally require Home Assistant account sign-in (`ha_auth`) for clients that support it.
300
+ - **Release channel:** the entry options let you pick `stable` (the pinned release) or `dev` (the latest development build, refreshed on every reload/restart).
301
+ - **The HA MCP Tools services entry is optional but recommended alongside it** — that second entry type of the same component provides the privileged file/YAML services the server's file tools use, exactly as with the add-on, Docker, and pip installs.
302
+
303
+ [Full in-process server documentation →](docs/in-process-server.md)
304
+
309
305
  ---
310
306
 
311
307
  ## 🧠 Better Results with Agent Skills
@@ -4,10 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.9.0.dev761"
7
+ version = "7.9.0.dev763"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
- requires-python = ">=3.13,<3.14"
10
+ # Upper bound widened to <3.15 so the package installs on Home Assistant's
11
+ # interpreter when run in-process by the ha_mcp_tools component (HA
12
+ # core-2026.7 ships Python 3.14). src/ stays 3.13-compatible.
13
+ requires-python = ">=3.13,<3.15"
11
14
  license = {text = "MIT"}
12
15
  authors = [
13
16
  {name = "Julien", email = "github@qc-h.net"}
@@ -19,6 +22,7 @@ classifiers = [
19
22
  "License :: OSI Approved :: MIT License",
20
23
  "Programming Language :: Python :: 3",
21
24
  "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
22
26
  "Topic :: Home Automation",
23
27
  "Topic :: Software Development :: Libraries :: Python Modules",
24
28
  ]
@@ -30,7 +34,13 @@ dependencies = [
30
34
  "python-dotenv==1.2.2",
31
35
  "truststore==0.10.4",
32
36
  "websockets==16.0",
33
- "cryptography==49.0.0",
37
+ # Range (not a hard pin) so the resolver can satisfy Home Assistant's own
38
+ # cryptography constraint when ha-mcp is installed into the HA venv for
39
+ # in-process embedding (HA 2026.6 pins cryptography==48.0.0, 2026.7 pins
40
+ # 48.0.1 — the floor must admit every supported core's pin; live-found on
41
+ # the embedded e2e lane against the HA 2026.6.4 image).
42
+ # cryptography is a transitive dependency only — never imported in src/.
43
+ "cryptography>=48.0.0,<50",
34
44
  "pydantic-monty==0.0.18",
35
45
  "tzdata>=2024.1",
36
46
  "packaging>=24.0",
@@ -194,6 +204,9 @@ dev = [
194
204
  "pytest-cov>=5.0.0",
195
205
  "pytest-timeout>=2.3.0",
196
206
  "pytest-rerunfailures>=15.0",
207
+ # Real voluptuous for the config-flow tests (the component's
208
+ # config_flow imports it; HA provides it at runtime, tests need it here).
209
+ "voluptuous>=0.15.2",
197
210
  "pytest-xdist>=3.8.0",
198
211
  "requests>=2.25.0",
199
212
  "lefthook>=1.10.0",
@@ -208,6 +221,11 @@ dev = [
208
221
  version_toml = ["pyproject.toml:project.version"]
209
222
  version_variables = [
210
223
  "src/ha_mcp/__init__.py:__version__",
224
+ # Keeps the in-process server's stable-channel pin in lockstep with the
225
+ # released server/add-on version - a hand-edited pin would drift the
226
+ # moment a release ships (owner: the server entry must always match the
227
+ # add-on version to avoid confusion).
228
+ "custom_components/ha_mcp_tools/const.py:PINNED_HA_MCP_VERSION",
211
229
  ]
212
230
  build_command = """
213
231
  pip install uv && uv lock
@@ -48,14 +48,34 @@ def is_dev_version(version: str) -> bool:
48
48
  return ".dev" in version
49
49
 
50
50
 
51
+ def is_embedded() -> bool:
52
+ """Return True when ha-mcp runs in-process inside Home Assistant core.
53
+
54
+ Set to ``1`` by the ha_mcp_tools custom component's in-process server entry
55
+ (``HA_MCP_EMBEDDED``) before the first ``ha_mcp`` import. On HAOS the
56
+ HA core container itself carries ``SUPERVISOR_TOKEN``, so without this flag
57
+ :func:`is_running_in_addon` would report True in-process and route log,
58
+ add-on-management, and settings-persistence logic down the add-on path. The
59
+ embedded server is an ordinary admin client of HA core (loopback URL +
60
+ provisioned token), so those call sites must take their non-add-on branch.
61
+ """
62
+ return bool(os.environ.get("HA_MCP_EMBEDDED"))
63
+
64
+
51
65
  def is_running_in_addon() -> bool:
52
66
  """Return True when running inside a Home Assistant add-on container.
53
67
 
54
68
  The HA Supervisor injects ``SUPERVISOR_TOKEN`` into every add-on's env.
55
69
  Checked so the standalone-Docker ``:stable`` banner isn't shown to add-on
56
70
  users, who already see the dev/stable distinction in the HAOS add-on UI.
71
+
72
+ Returns False in embedded mode (:func:`is_embedded`) even though the HA core
73
+ container carries ``SUPERVISOR_TOKEN``: the in-process server is a plain
74
+ admin client of HA core, not a Supervisor-managed add-on, so add-on-only
75
+ behavior (Supervisor-direct log fetch, add-on settings routing) must not
76
+ apply.
57
77
  """
58
- return bool(os.environ.get("SUPERVISOR_TOKEN"))
78
+ return bool(os.environ.get("SUPERVISOR_TOKEN")) and not is_embedded()
59
79
 
60
80
 
61
81
  def get_supervisor_base_url() -> str:
@@ -14,7 +14,7 @@ from dotenv import load_dotenv
14
14
  from pydantic import Field, ValidationInfo, field_validator
15
15
  from pydantic_settings import BaseSettings, SettingsConfigDict
16
16
 
17
- from ha_mcp._version import get_version
17
+ from ha_mcp._version import get_version, is_running_in_addon
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
@@ -913,7 +913,12 @@ def get_feature_flag_origin(env_name: str) -> str:
913
913
  )
914
914
  is_master = field_name == "enable_beta_features"
915
915
  is_beta_sub = field_name in BETA_FEATURE_FIELDS
916
- in_addon = bool(os.environ.get("SUPERVISOR_TOKEN"))
916
+ # is_running_in_addon() (not a raw SUPERVISOR_TOKEN read) so the in-process
917
+ # embedded server — which carries SUPERVISOR_TOKEN on HAOS but is not an
918
+ # add-on — is treated as a standalone deployment: its settings-UI edits
919
+ # persist to override files under HA_MCP_CONFIG_DIR instead of being routed
920
+ # to a Supervisor add-on that does not exist.
921
+ in_addon = is_running_in_addon()
917
922
 
918
923
  if in_addon:
919
924
  if is_master or is_beta_sub:
@@ -1034,7 +1039,11 @@ def _apply_feature_flag_overrides(settings: "Settings") -> None:
1034
1039
  env var still needs to flip the master before the flag takes
1035
1040
  effect.
1036
1041
  """
1037
- in_addon = bool(os.environ.get("SUPERVISOR_TOKEN"))
1042
+ # is_running_in_addon() (not a raw SUPERVISOR_TOKEN read) so the in-process
1043
+ # embedded server — which carries SUPERVISOR_TOKEN on HAOS but is not an
1044
+ # add-on — applies its settings-UI feature-flag saves like a standalone
1045
+ # deployment instead of short-circuiting here as if start.py owned the env.
1046
+ in_addon = is_running_in_addon()
1038
1047
  overrides = _read_feature_flag_override_file()
1039
1048
 
1040
1049
  known = {fname: (ename, ftype) for fname, ename, ftype in FEATURE_FLAG_FIELDS}
@@ -1268,6 +1277,48 @@ def _apply_advanced_overrides(settings: "Settings") -> None:
1268
1277
  # Global settings instance
1269
1278
  _settings: Settings | None = None
1270
1279
 
1280
+ # In-process (embedded) HA connection, set only when ha-mcp runs inside Home
1281
+ # Assistant core via the ha_mcp_tools custom component's in-process server entry.
1282
+ # The component hands the loopback URL + a provisioned admin token to ha-mcp
1283
+ # THROUGH THIS DICT — never
1284
+ # via os.environ — so the admin token can't be read from the shared HA process
1285
+ # environment. Applied onto the Settings singleton in ``get_global_settings``.
1286
+ _EMBEDDED_CONNECTION: dict[str, str] = {}
1287
+
1288
+
1289
+ def set_embedded_connection(url: str, token: str) -> None:
1290
+ """Register the in-process HA connection for embedded mode.
1291
+
1292
+ Embedded-mode only: called by the ha_mcp_tools custom component's in-process
1293
+ server entry inside its server worker thread, before the server is
1294
+ constructed, so the loopback URL
1295
+ and admin token reach ``Settings`` in memory instead of through ``os.environ``.
1296
+ The values survive ``_reset_global_settings()``: the settings-UI reset+rebuild
1297
+ path re-applies them on the next ``get_global_settings()`` call.
1298
+
1299
+ Also applies to an ALREADY-BUILT singleton: importing ``ha_mcp`` runs the
1300
+ package's eager import chain, and ``tools/smart_search/_config.py`` builds
1301
+ the settings singleton at import time (its documented read-once budgets).
1302
+ Registration therefore cannot assume it runs before the first build — the
1303
+ integration imports this function from the very package whose import
1304
+ creates the singleton.
1305
+ """
1306
+ _EMBEDDED_CONNECTION["url"] = url
1307
+ _EMBEDDED_CONNECTION["token"] = token
1308
+ if _settings is not None:
1309
+ _apply_embedded_connection(_settings)
1310
+
1311
+
1312
+ def _reset_embedded_connection() -> None:
1313
+ """Drop the registered embedded connection (test seam).
1314
+
1315
+ Sibling to :func:`_reset_global_settings`; lets suites that exercise the
1316
+ in-process token channel isolate state between tests. Not used in production —
1317
+ the connection is registered once per worker thread and is meant to persist.
1318
+ """
1319
+ _EMBEDDED_CONNECTION.clear()
1320
+
1321
+
1271
1322
  # Names of beta sub-flags the master gate has already logged a
1272
1323
  # force-False line for in this process. Used to dedup the gate's
1273
1324
  # INFO log so we don't spam addon logs on every Settings rebuild
@@ -1332,7 +1383,11 @@ def get_backup_setting_origin(env_name: str) -> str:
1332
1383
  - ``"default"``: no env var and no override file entry; the
1333
1384
  pydantic field default applies. Web UI edits create the file.
1334
1385
  """
1335
- if os.environ.get("SUPERVISOR_TOKEN"):
1386
+ # is_running_in_addon() rather than a raw SUPERVISOR_TOKEN read so the
1387
+ # embedded in-process server (SUPERVISOR_TOKEN present on HAOS, but not an
1388
+ # add-on) is labeled a standalone deployment and its backup settings persist
1389
+ # to the override file instead of a non-existent Supervisor add-on.
1390
+ if is_running_in_addon():
1336
1391
  return "addon"
1337
1392
  if os.environ.get(env_name) is not None:
1338
1393
  return "env"
@@ -1424,7 +1479,10 @@ def _apply_backup_overrides(settings: "Settings") -> None:
1424
1479
  corrupt override file can't push values out of range; out-of-range
1425
1480
  or untypable entries are silently skipped.
1426
1481
  """
1427
- if os.environ.get("SUPERVISOR_TOKEN"):
1482
+ # is_running_in_addon() (embedded-aware) so the in-process server applies
1483
+ # its backup override file like a standalone deployment; see the matching
1484
+ # rationale in _apply_feature_flag_overrides / get_backup_setting_origin.
1485
+ if is_running_in_addon():
1428
1486
  return
1429
1487
  overrides = _read_backup_override_file()
1430
1488
  if not overrides:
@@ -1522,6 +1580,22 @@ def _apply_backup_overrides(settings: "Settings") -> None:
1522
1580
  continue
1523
1581
 
1524
1582
 
1583
+ def _apply_embedded_connection(settings: "Settings") -> None:
1584
+ """Apply the in-process embedded HA connection (url/token) if registered.
1585
+
1586
+ No-op outside embedded mode. Plain ``setattr`` (``validate_assignment`` is off
1587
+ on ``Settings``, mirroring ``_apply_backup_overrides``), so the loopback URL
1588
+ and admin token are set in memory without ever passing through ``os.environ``.
1589
+ Applied last so it wins over any env/override-file connection values.
1590
+ """
1591
+ url = _EMBEDDED_CONNECTION.get("url")
1592
+ token = _EMBEDDED_CONNECTION.get("token")
1593
+ if url:
1594
+ settings.homeassistant_url = url.rstrip("/")
1595
+ if token:
1596
+ settings.homeassistant_token = token
1597
+
1598
+
1525
1599
  def get_global_settings() -> Settings:
1526
1600
  """Get global settings instance (singleton pattern).
1527
1601
 
@@ -1531,6 +1605,10 @@ def get_global_settings() -> Settings:
1531
1605
 
1532
1606
  - Feature flags persisted to ``<data_dir>/feature_flags.json``
1533
1607
  - Auto-backup settings persisted to ``<data_dir>/backup_settings.json``
1608
+
1609
+ In embedded mode, the in-process HA connection registered via
1610
+ ``set_embedded_connection`` is applied last (so a settings-UI reset+rebuild
1611
+ re-picks it up).
1534
1612
  """
1535
1613
  global _settings
1536
1614
  if _settings is None:
@@ -1538,9 +1616,22 @@ def get_global_settings() -> Settings:
1538
1616
  _apply_feature_flag_overrides(_settings)
1539
1617
  _apply_backup_overrides(_settings)
1540
1618
  _apply_advanced_overrides(_settings)
1619
+ _apply_embedded_connection(_settings)
1541
1620
  return _settings
1542
1621
 
1543
1622
 
1623
+ def reset_global_settings() -> None:
1624
+ """Public seam to drop the cached settings singleton.
1625
+
1626
+ The in-process (embedded) server calls this at every start: a config-entry
1627
+ reload reuses the same Python process, so without an explicit reset the
1628
+ singleton built on the FIRST start would keep serving stale feature-flag /
1629
+ override values forever (the add-on gets fresh settings for free from its
1630
+ process restart).
1631
+ """
1632
+ _reset_global_settings()
1633
+
1634
+
1544
1635
  def _reset_global_settings() -> None:
1545
1636
  """Drop the cached settings singleton.
1546
1637
 
@@ -156,7 +156,7 @@
156
156
  main {
157
157
  max-width: 1280px;
158
158
  margin: 0 auto;
159
- padding: 0 16px 32px;
159
+ padding: 0 16px 40px;
160
160
  }
161
161
 
162
162
  /* ── SKIP LINK ───────────────────────────────────────────────────────── */
@@ -209,7 +209,7 @@
209
209
  }
210
210
  .tab:hover { color: var(--text); background: rgba(255, 255, 255, 0.04); }
211
211
  .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
212
- .panel { display: none; padding-top: 16px; }
212
+ .panel { display: none; padding-top: 24px; }
213
213
  .panel.active { display: block; }
214
214
 
215
215
  /* ── ALERTS (HA ha-alert) ────────────────────────────────────────────── */
@@ -309,7 +309,7 @@
309
309
  .group {
310
310
  background: var(--surface);
311
311
  border-radius: 12px;
312
- margin-bottom: 8px;
312
+ margin-bottom: 12px;
313
313
  overflow: hidden;
314
314
  border: var(--card-border);
315
315
  box-shadow: var(--card-shadow);
@@ -336,7 +336,7 @@
336
336
  /* ── INDIVIDUAL TOOLS ────────────────────────────────────────────────── */
337
337
  .tool {
338
338
  display: flex; align-items: center; justify-content: space-between;
339
- padding: 10px 16px; border-bottom: 1px solid var(--border);
339
+ padding: 12px 16px; border-bottom: 1px solid var(--border);
340
340
  transition: background 0.1s;
341
341
  }
342
342
  .tool:last-child { border-bottom: none; }
@@ -463,18 +463,29 @@
463
463
 
464
464
  /* ── SERVER SETTINGS FEATURES ────────────────────────────────────────── */
465
465
  .features-sub {
466
- font-size: 0.75rem; color: var(--text-secondary);
466
+ font-size: 0.8125rem; color: var(--text-secondary);
467
467
  margin-bottom: 12px; line-height: 1.5;
468
468
  }
469
+ /* Server Settings feature list, grouped into a card (parity with the tool
470
+ groups + accessibility sections) so the tab reads as organized cards
471
+ rather than a flat wall of borderless rows. */
472
+ #featuresBody {
473
+ background: var(--surface);
474
+ border: var(--card-border);
475
+ box-shadow: var(--card-shadow);
476
+ border-radius: 12px;
477
+ padding: 4px 16px;
478
+ margin-bottom: 8px;
479
+ }
469
480
  .feature-row {
470
481
  display: flex; align-items: flex-start; justify-content: space-between;
471
- gap: 16px; padding: 14px 0; border-top: 1px solid var(--border);
482
+ gap: 16px; padding: 16px 0; border-top: 1px solid var(--border);
472
483
  }
473
484
  .feature-row:first-of-type { border-top: none; }
474
485
  .feature-info { flex: 1; min-width: 0; }
475
486
  .feature-name { font-size: 0.875rem; font-weight: 500; }
476
487
  .feature-help {
477
- font-size: 0.75rem; color: var(--text-secondary);
488
+ font-size: 0.8125rem; color: var(--text-secondary);
478
489
  margin-top: 4px; line-height: 1.5;
479
490
  }
480
491
  .feature-help code {
@@ -509,7 +520,7 @@
509
520
  /* ── ADVANCED SETTINGS ───────────────────────────────────────────────── */
510
521
  .adv-section-title {
511
522
  font-size: 0.75rem; font-weight: 500; color: var(--text-secondary);
512
- margin: 24px 0 4px; text-transform: uppercase; letter-spacing: 0.08em;
523
+ margin: 28px 0 8px; text-transform: uppercase; letter-spacing: 0.08em;
513
524
  }
514
525
  .adv-section-title.beta-section-title {
515
526
  color: var(--warning); font-size: 0.8rem; margin-top: 32px;
@@ -530,7 +541,17 @@
530
541
  .adv-save-btn:hover { background: var(--accent-hover); }
531
542
  .adv-save-btn:active { filter: brightness(0.9); }
532
543
  .adv-save-btn:disabled { opacity: 0.38; cursor: not-allowed; }
533
- .adv-section { border-top: 1px solid var(--border); }
544
+ .adv-section {
545
+ background: var(--surface);
546
+ border: var(--card-border);
547
+ box-shadow: var(--card-shadow);
548
+ border-radius: 12px;
549
+ padding: 4px 16px;
550
+ margin-bottom: 8px;
551
+ }
552
+ /* A flag-gated section can render with no fields; hide the empty <div> so it
553
+ doesn't show as a hollow card (its overline title above stays as a label). */
554
+ .adv-section:empty { display: none; }
534
555
  .adv-section.dimmed { opacity: 0.38; }
535
556
  .adv-section.dimmed input,
536
557
  .adv-section.dimmed select { cursor: not-allowed; }
@@ -538,13 +559,13 @@
538
559
  .adv-section-note { font-size: 0.72rem; color: var(--text-secondary); margin: 4px 0 8px; line-height: 1.5; }
539
560
  .adv-row {
540
561
  display: flex; align-items: flex-start; justify-content: space-between;
541
- gap: 16px; padding: 12px 0; border-top: 1px solid var(--border);
562
+ gap: 16px; padding: 14px 0; border-top: 1px solid var(--border);
542
563
  }
543
564
  .adv-row:first-child { border-top: none; }
544
565
  .adv-row.locked .adv-name { color: var(--text-secondary); }
545
566
  .adv-info { flex: 1; min-width: 0; }
546
567
  .adv-name { font-size: 0.875rem; font-weight: 500; }
547
- .adv-help { font-size: 0.75rem; color: var(--text-secondary); margin-top: 4px; line-height: 1.5; }
568
+ .adv-help { font-size: 0.8125rem; color: var(--text-secondary); margin-top: 4px; line-height: 1.5; }
548
569
  .adv-help code {
549
570
  background: var(--code-bg); padding: 1px 5px; border-radius: 3px;
550
571
  font-size: 0.7rem; font-family: 'Roboto Mono', 'Courier New', monospace;
@@ -663,6 +684,11 @@
663
684
  .diff-hdr { color: var(--diff-hdr); }
664
685
 
665
686
  /* ── TOOL SECURITY POLICIES ──────────────────────────────────────────── */
687
+ /* Bare tab title (no class hook in the markup): give it a defined heading
688
+ size so it doesn't fall back to the oversized browser default h2. */
689
+ #panel-tool-security-policies > h2 {
690
+ font-size: 1.125rem; font-weight: 500; margin-bottom: 8px;
691
+ }
666
692
  .policy-rule-card {
667
693
  background: var(--surface); border: var(--card-border); border-radius: 12px;
668
694
  padding: 14px 16px; margin: 8px 0;
@@ -721,7 +747,7 @@
721
747
  padding: 16px; margin-bottom: 12px;
722
748
  }
723
749
  .a11y-section-title { font-size: 0.9375rem; font-weight: 500; margin-bottom: 4px; }
724
- .a11y-section-help { font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 12px; }
750
+ .a11y-section-help { font-size: 0.8125rem; color: var(--text-secondary); margin-bottom: 12px; }
725
751
  .a11y-options { display: flex; flex-wrap: wrap; gap: 8px; }
726
752
  fieldset.a11y-options { border: 0; padding: 0; margin: 0; min-inline-size: auto; }
727
753
  .visually-hidden {
@@ -889,3 +915,41 @@
889
915
  border-bottom-color: var(--accent);
890
916
  box-shadow: inset 0 -1px 0 0 var(--accent);
891
917
  }
918
+
919
+ /* ── NARROW SCREENS (phones / small tablets) ─────────────────────────────
920
+ The layout was desktop-only (label left, control right on one line). On a
921
+ phone the label + help + control were crammed into a sliver. Below 640px,
922
+ stack each setting row so the control sits on its own line under the help,
923
+ make text fields fill the width, tighten the page gutters, and drop the
924
+ decorative header icon. Nothing here changes hooks or semantics. */
925
+ @media (max-width: 640px) {
926
+ main { padding: 0 12px 40px; }
927
+ .header { padding: 0 12px; }
928
+ .header-icon { display: none; }
929
+ .header h1 { font-size: 1.0625rem; }
930
+ .panel { padding-top: 20px; }
931
+
932
+ /* One control per line, aligned under its label + help. */
933
+ .feature-row,
934
+ .adv-row {
935
+ flex-direction: column;
936
+ align-items: stretch;
937
+ gap: 10px;
938
+ }
939
+ .feature-control,
940
+ .adv-control { width: 100%; justify-content: flex-start; }
941
+ .adv-control input[type="text"],
942
+ .adv-control select { width: 100%; }
943
+
944
+ /* Backups: stack the row body above its actions; drop the desktop indents. */
945
+ .backup-row { flex-direction: column; align-items: stretch; }
946
+ .backup-row-actions { flex-wrap: wrap; }
947
+ .backup-field-label { min-width: 0; }
948
+ .backup-field-help { margin-left: 0; }
949
+
950
+ /* Policy predicate form: the desktop hint indent has no room here. */
951
+ .policy-predicate-form .policy-form-hint { padding-left: 0; }
952
+
953
+ /* Modal fills the small viewport instead of floating in a sliver. */
954
+ .modal { max-height: 94vh; }
955
+ }