ha-mcp-dev 7.6.0.dev676__tar.gz → 7.6.0.dev677__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 (140) hide show
  1. {ha_mcp_dev-7.6.0.dev676/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev677}/PKG-INFO +5 -3
  2. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/README.md +4 -2
  3. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/pyproject.toml +1 -1
  4. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/settings.js +152 -0
  5. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/settings_ui.py +158 -0
  6. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/stdio_settings_sidecar.py +15 -0
  7. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_filesystem.py +5 -20
  8. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677/src/ha_mcp_dev.egg-info}/PKG-INFO +5 -3
  9. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/LICENSE +0 -0
  10. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/MANIFEST.in +0 -0
  11. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/setup.cfg +0 -0
  12. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/__init__.py +0 -0
  13. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/__main__.py +0 -0
  14. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/_pypi_marker +0 -0
  15. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/_version.py +0 -0
  16. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/auth/__init__.py +0 -0
  17. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/auth/consent_form.py +0 -0
  18. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/auth/provider.py +0 -0
  19. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/backup_manager.py +0 -0
  20. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/client/__init__.py +0 -0
  21. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/client/rest_client.py +0 -0
  22. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/client/supervisor_client.py +0 -0
  23. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/client/websocket_client.py +0 -0
  24. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/client/websocket_listener.py +0 -0
  25. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/config.py +0 -0
  26. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  27. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  28. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  29. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/errors.py +0 -0
  30. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/__init__.py +0 -0
  31. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/approval_queue.py +0 -0
  32. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/evaluator.py +0 -0
  33. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/handlers.py +0 -0
  34. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/middleware.py +0 -0
  35. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/model.py +0 -0
  36. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/persistence.py +0 -0
  37. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/policy/value_sources.py +0 -0
  38. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/py.typed +0 -0
  39. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  40. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  41. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  42. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  43. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  44. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  45. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  46. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  47. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  48. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  49. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  50. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  51. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  52. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  53. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  54. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  55. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  56. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  57. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  58. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  59. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  60. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  61. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/server.py +0 -0
  62. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/settings.css +0 -0
  63. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/smoke_test.py +0 -0
  64. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/__init__.py +0 -0
  65. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/auto_backup.py +0 -0
  66. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/backup.py +0 -0
  67. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  68. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/device_control.py +0 -0
  69. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/enhanced.py +0 -0
  70. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/helpers.py +0 -0
  71. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/reference_validator.py +0 -0
  72. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/registry.py +0 -0
  73. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  80. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  81. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  82. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_addons.py +0 -0
  83. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_areas.py +0 -0
  84. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  85. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  86. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_calendar.py +0 -0
  87. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_camera.py +0 -0
  88. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_categories.py +0 -0
  89. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_code.py +0 -0
  90. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  91. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  92. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  93. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  94. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  95. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  96. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  97. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_energy.py +0 -0
  98. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_entities.py +0 -0
  99. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_groups.py +0 -0
  100. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_hacs.py +0 -0
  101. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_history.py +0 -0
  102. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_integrations.py +0 -0
  103. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_labels.py +0 -0
  104. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_registry.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_resources.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_search.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_service.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_services.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_system.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_todo.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_traces.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_updates.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_utility.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/tools_zones.py +0 -0
  118. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/util_helpers.py +0 -0
  119. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/tools/validation_middleware.py +0 -0
  120. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/transforms/__init__.py +0 -0
  121. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/transforms/categorized_search.py +0 -0
  122. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/__init__.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/config_hash.py +0 -0
  125. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/data_paths.py +0 -0
  126. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/domain_handlers.py +0 -0
  127. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  128. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  129. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/operation_manager.py +0 -0
  130. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/python_sandbox.py +0 -0
  131. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/skill_loader.py +0 -0
  132. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp/utils/usage_logger.py +0 -0
  133. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  134. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  135. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  136. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  137. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  138. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/tests/__init__.py +0 -0
  139. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/tests/test_constants.py +0 -0
  140. {ha_mcp_dev-7.6.0.dev676 → ha_mcp_dev-7.6.0.dev677}/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.6.0.dev676
3
+ Version: 7.6.0.dev677
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
@@ -372,7 +372,7 @@ For comprehensive testing documentation, see **[tests/README.md](tests/README.md
372
372
 
373
373
  Ha-mcp runs **locally** on your machine. Your smart home data stays on your network.
374
374
 
375
- - **No telemetry today** — anonymous usage stats are a planned future feature (as of May 2026); users will be notified when it lands and it will be opt-in only
375
+ - **No telemetry today** — anonymous usage stats are a planned future feature (as of June 2026); when it lands it will follow your Home Assistant analytics/telemetry setting (which you can override), announced prominently in the release notes and the web Settings UI at least one month beforehand
376
376
  - **No personal data collection** — we never collect entity names, configs, or device data
377
377
  - **User-controlled bug reports** — only sent with your explicit approval
378
378
 
@@ -435,13 +435,15 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
435
435
  - **[@griffinmartin](https://github.com/griffinmartin)** — Added OpenCode (by Anomaly) as a selectable AI client in the setup wizard, with both stdio and streamable HTTP support.
436
436
  - **[@hhopke](https://github.com/hhopke)** — Fixed addon API calls to route through HA Core ingress proxy instead of direct container connections, fixing `ha_manage_addon` proxy mode on addon installs.
437
437
  - **[@tomwilkie](https://github.com/tomwilkie)** — JMESPath middleware exploration (#1147) whose review-time token-measurement data informed the design of #1199 and #1225.
438
- - **[@SealKan](https://github.com/SealKan)** — `fields=`/`attribute_keys=` projection on six read-heavy tools (#1225), `ha_call_event` tool (#1239), and dashboards-list helper refactor (#1207).
438
+ - **[@SealKan](https://github.com/SealKan)** — `fields=`/`attribute_keys=` projection on six read-heavy tools (#1225), `ha_call_event` tool (#1239), dashboards-list helper refactor (#1207), `for:`-field duration-math detector in the best-practice checker (#1264), persistent DCR OAuth client registrations across restarts (#1265), and issue-triage prompt token-budgeting (#1522).
439
439
  - **[@KarelTestSpecial](https://github.com/KarelTestSpecial)** — Cached YAML instance to prevent CPU spikes during bulk edits (#1371).
440
440
  - **[@corgan2222](https://github.com/corgan2222)** — HA brand assets for custom integration (#1317).
441
441
  - **[@drseanwing](https://github.com/drseanwing)** — Progress emission via FastMCP `Context` in long-running tools (#1124); tool-discovery / categorized-search docs (#1123).
442
442
  - **[@fnordpig](https://github.com/fnordpig)** — Config subentry support (#1393) and Assist pipeline management tool (#1392).
443
443
  - **[@paul43210](https://github.com/paul43210)** — `array_patch` mode in `ha_manage_addon` for atomic GET-modify-POST (#1063).
444
444
  - **[@L1AD](https://github.com/L1AD)** — Filed #966 proposing tool security policies; pointed to PolicyLayer's MCP-security work as prior art that inspired the predicate DSL shape.
445
+ - **[@nightcityblade](https://github.com/nightcityblade)** — Updated stale Home Assistant Advanced Mode references after HA 2026.6 made formerly advanced options available by default (#1533).
446
+ - **[@emmelutzer](https://github.com/emmelutzer)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
445
447
 
446
448
  ---
447
449
 
@@ -342,7 +342,7 @@ For comprehensive testing documentation, see **[tests/README.md](tests/README.md
342
342
 
343
343
  Ha-mcp runs **locally** on your machine. Your smart home data stays on your network.
344
344
 
345
- - **No telemetry today** — anonymous usage stats are a planned future feature (as of May 2026); users will be notified when it lands and it will be opt-in only
345
+ - **No telemetry today** — anonymous usage stats are a planned future feature (as of June 2026); when it lands it will follow your Home Assistant analytics/telemetry setting (which you can override), announced prominently in the release notes and the web Settings UI at least one month beforehand
346
346
  - **No personal data collection** — we never collect entity names, configs, or device data
347
347
  - **User-controlled bug reports** — only sent with your explicit approval
348
348
 
@@ -405,13 +405,15 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
405
405
  - **[@griffinmartin](https://github.com/griffinmartin)** — Added OpenCode (by Anomaly) as a selectable AI client in the setup wizard, with both stdio and streamable HTTP support.
406
406
  - **[@hhopke](https://github.com/hhopke)** — Fixed addon API calls to route through HA Core ingress proxy instead of direct container connections, fixing `ha_manage_addon` proxy mode on addon installs.
407
407
  - **[@tomwilkie](https://github.com/tomwilkie)** — JMESPath middleware exploration (#1147) whose review-time token-measurement data informed the design of #1199 and #1225.
408
- - **[@SealKan](https://github.com/SealKan)** — `fields=`/`attribute_keys=` projection on six read-heavy tools (#1225), `ha_call_event` tool (#1239), and dashboards-list helper refactor (#1207).
408
+ - **[@SealKan](https://github.com/SealKan)** — `fields=`/`attribute_keys=` projection on six read-heavy tools (#1225), `ha_call_event` tool (#1239), dashboards-list helper refactor (#1207), `for:`-field duration-math detector in the best-practice checker (#1264), persistent DCR OAuth client registrations across restarts (#1265), and issue-triage prompt token-budgeting (#1522).
409
409
  - **[@KarelTestSpecial](https://github.com/KarelTestSpecial)** — Cached YAML instance to prevent CPU spikes during bulk edits (#1371).
410
410
  - **[@corgan2222](https://github.com/corgan2222)** — HA brand assets for custom integration (#1317).
411
411
  - **[@drseanwing](https://github.com/drseanwing)** — Progress emission via FastMCP `Context` in long-running tools (#1124); tool-discovery / categorized-search docs (#1123).
412
412
  - **[@fnordpig](https://github.com/fnordpig)** — Config subentry support (#1393) and Assist pipeline management tool (#1392).
413
413
  - **[@paul43210](https://github.com/paul43210)** — `array_patch` mode in `ha_manage_addon` for atomic GET-modify-POST (#1063).
414
414
  - **[@L1AD](https://github.com/L1AD)** — Filed #966 proposing tool security policies; pointed to PolicyLayer's MCP-security work as prior art that inspired the predicate DSL shape.
415
+ - **[@nightcityblade](https://github.com/nightcityblade)** — Updated stale Home Assistant Advanced Mode references after HA 2026.6 made formerly advanced options available by default (#1533).
416
+ - **[@emmelutzer](https://github.com/emmelutzer)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
415
417
 
416
418
  ---
417
419
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.6.0.dev676"
7
+ version = "7.6.0.dev677"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -888,6 +888,149 @@ async function saveBackupConfig() {
888
888
  }
889
889
  }
890
890
 
891
+ // ---- Custom filesystem directories (issue #1567) ---------------------------
892
+ // The list lives in the ha_mcp_tools custom component; the GET/POST endpoints
893
+ // proxy to it via authenticated HA service calls. Cached so the sub-form
894
+ // re-renders synchronously when the beta master / filesystem-tools toggle
895
+ // flips, without re-fetching.
896
+ // Consumed fields of the GET response: {available, paths, deny_floor, reason}
897
+ // (the endpoint also returns builtin_read_dirs/builtin_write_dirs, unused here).
898
+ let _fsCustomPathsData = null;
899
+
900
+ async function loadFsCustomPaths() {
901
+ try {
902
+ const resp = await fetch('./api/settings/fs-custom-paths');
903
+ if (!resp.ok) {
904
+ _fsCustomPathsData = {
905
+ available: false,
906
+ reason: `HTTP ${resp.status}`,
907
+ paths: [],
908
+ deny_floor: [],
909
+ };
910
+ } else {
911
+ _fsCustomPathsData = await resp.json();
912
+ }
913
+ } catch (err) {
914
+ _fsCustomPathsData = {
915
+ available: false,
916
+ reason: 'Network error: ' + String(err),
917
+ paths: [],
918
+ deny_floor: [],
919
+ };
920
+ }
921
+ // Re-render the feature panel so the sub-form reflects the loaded data.
922
+ if (Object.keys(_lastFeatureFlags).length) renderFeatureFlags(_lastFeatureFlags);
923
+ }
924
+
925
+ // Injected beneath the enable_filesystem_tools row in renderFeatureFlags.
926
+ // Second-level nested (under filesystem tools, itself beta-sub-nested under
927
+ // the master), dimmed when either the master beta or filesystem tools is off.
928
+ function renderFsCustomPathsSubForm(parentEl, masterOn, fsOn) {
929
+ const lockedByGate = !masterOn || !fsOn;
930
+ const d = _fsCustomPathsData;
931
+ const row = document.createElement('div');
932
+ row.className =
933
+ 'feature-row fs-custom-paths-sub' + (lockedByGate ? ' dimmed' : '');
934
+
935
+ const info = document.createElement('div');
936
+ info.className = 'feature-info';
937
+ const denyList =
938
+ d && Array.isArray(d.deny_floor) && d.deny_floor.length
939
+ ? d.deny_floor.join(', ')
940
+ : '.storage, secrets.yaml';
941
+ info.innerHTML =
942
+ `<div class="feature-name">Custom filesystem directories (advanced)</div>` +
943
+ `<div class="feature-help">Extra directories (one per line, relative to your config dir) that the file tools may READ and WRITE — e.g. <code>pyscript</code>, <code>python_scripts</code>. Each entry grants both read and write. Applies immediately; no restart needed.</div>` +
944
+ `<div class="feature-help">Always blocked (cannot be added): <code>${escapeHtml(denyList)}</code>, path traversal (<code>..</code>), and absolute paths.</div>`;
945
+
946
+ const control = document.createElement('div');
947
+ control.className = 'feature-control';
948
+
949
+ if (lockedByGate) {
950
+ const note = document.createElement('div');
951
+ note.className = 'feature-locked-note';
952
+ note.textContent =
953
+ 'Enable beta features and filesystem tools above to edit.';
954
+ control.appendChild(note);
955
+ } else if (!d) {
956
+ const note = document.createElement('div');
957
+ note.className = 'feature-help';
958
+ note.textContent = 'Loading…';
959
+ control.appendChild(note);
960
+ } else if (!d.available) {
961
+ const note = document.createElement('div');
962
+ note.className = 'feature-locked-note';
963
+ note.textContent =
964
+ d.reason || 'Custom directories are currently unavailable.';
965
+ control.appendChild(note);
966
+ } else {
967
+ const ta = document.createElement('textarea');
968
+ ta.id = 'fsCustomPathsInput';
969
+ ta.rows = 4;
970
+ ta.value = (d.paths || []).join('\n');
971
+ const btn = document.createElement('button');
972
+ btn.id = 'fsCustomPathsSave';
973
+ btn.className = 'adv-save-btn';
974
+ btn.textContent = 'Save directories';
975
+ btn.addEventListener('click', saveFsCustomPaths);
976
+ const status = document.createElement('div');
977
+ status.id = 'fsCustomPathsStatus';
978
+ status.className = 'feature-help';
979
+ control.appendChild(ta);
980
+ control.appendChild(btn);
981
+ control.appendChild(status);
982
+ }
983
+
984
+ row.appendChild(info);
985
+ row.appendChild(control);
986
+ parentEl.appendChild(row);
987
+ }
988
+
989
+ async function saveFsCustomPaths() {
990
+ const ta = document.getElementById('fsCustomPathsInput');
991
+ const btn = document.getElementById('fsCustomPathsSave');
992
+ const statusEl = document.getElementById('fsCustomPathsStatus');
993
+ if (!ta || !btn || !statusEl) return;
994
+ const paths = ta.value
995
+ .split('\n')
996
+ .map(s => s.trim())
997
+ .filter(s => s.length);
998
+ btn.disabled = true;
999
+ statusEl.textContent = 'Saving…';
1000
+ try {
1001
+ const resp = await fetch('./api/settings/fs-custom-paths', {
1002
+ method: 'POST',
1003
+ headers: { 'Content-Type': 'application/json' },
1004
+ body: JSON.stringify({ paths }),
1005
+ });
1006
+ const data = await resp.json();
1007
+ btn.disabled = false;
1008
+ if (!resp.ok || !data.success) {
1009
+ let msg = 'Save failed';
1010
+ if (data && data.error) {
1011
+ if (typeof data.error === 'string') msg = data.error;
1012
+ else if (data.error.message) msg = data.error.message;
1013
+ }
1014
+ statusEl.textContent = msg;
1015
+ return;
1016
+ }
1017
+ // Reflect the component's canonical normalized list; drop any rejected.
1018
+ _fsCustomPathsData = {
1019
+ ...(_fsCustomPathsData || {}),
1020
+ available: true,
1021
+ paths: data.paths || [],
1022
+ };
1023
+ ta.value = (data.paths || []).join('\n');
1024
+ const rejected = data.rejected || [];
1025
+ statusEl.textContent = rejected.length
1026
+ ? `Saved. Rejected (blocked or invalid): ${rejected.join(', ')}`
1027
+ : 'Saved.';
1028
+ } catch (err) {
1029
+ btn.disabled = false;
1030
+ statusEl.textContent = 'Network error: ' + String(err);
1031
+ }
1032
+ }
1033
+
891
1034
  async function loadBackups() {
892
1035
  const params = new URLSearchParams();
893
1036
  const d = document.getElementById('backupDomain').value.trim();
@@ -1351,6 +1494,14 @@ function renderFeatureFlags(flags) {
1351
1494
  const parentOn = !!f.value;
1352
1495
  renderYamlPackagesSubRows(flags, targetBody, masterOn, parentOn);
1353
1496
  }
1497
+ // After the enable_filesystem_tools row, inject the custom-directories
1498
+ // editor (issue #1567). Dimmed when either the master beta is off or
1499
+ // filesystem tools itself is off. The list is component-owned and fetched
1500
+ // separately via loadFsCustomPaths(); this renders from that cache.
1501
+ if (fieldName === 'enable_filesystem_tools') {
1502
+ const fsOn = !!f.value;
1503
+ renderFsCustomPathsSubForm(targetBody, masterOn, fsOn);
1504
+ }
1354
1505
  });
1355
1506
  }
1356
1507
 
@@ -2718,6 +2869,7 @@ document.getElementById('advSaveBtn').addEventListener('click', saveAdvancedSett
2718
2869
  loadFeatureFlags();
2719
2870
  loadAdvancedSettings();
2720
2871
  loadTools();
2872
+ loadFsCustomPaths();
2721
2873
 
2722
2874
  // Auto-activate tab from ?tab=<name> query string (used by approval URLs
2723
2875
  // generated by the policy middleware: /settings?tab=tool-security-policies&token=...).
@@ -2912,6 +2912,159 @@ def build_settings_handlers(
2912
2912
  }
2913
2913
  )
2914
2914
 
2915
+ async def _fs_custom_paths_call(service: str, data: dict[str, Any]) -> Any:
2916
+ """Invoke a ha_mcp_tools component service for the custom-paths editor,
2917
+ in any deployment mode (issue #1567).
2918
+
2919
+ Uses the live server's HA client in HTTP/add-on modes; in the stdio
2920
+ sidecar (``server is None``) builds a transient ``HomeAssistantClient``
2921
+ from the HA URL/token the sidecar inherits, and closes it afterward.
2922
+ The caller wraps this in try/except so an unreachable HA / missing
2923
+ token (e.g. OAuth mode, where ``server.client`` has no request-scoped
2924
+ token) degrades to an "unavailable" envelope rather than a 500.
2925
+ """
2926
+ from .tools.tools_filesystem import call_mcp_tools_service
2927
+
2928
+ own_client = None
2929
+ try:
2930
+ if server is not None:
2931
+ client = server.client
2932
+ else:
2933
+ from .client.rest_client import HomeAssistantClient
2934
+
2935
+ client = own_client = HomeAssistantClient()
2936
+ return await call_mcp_tools_service(client, service, data)
2937
+ finally:
2938
+ if own_client is not None and hasattr(own_client, "close"):
2939
+ with contextlib.suppress(Exception):
2940
+ await own_client.close()
2941
+
2942
+ async def _get_fs_custom_paths(_: Request) -> JSONResponse:
2943
+ """Return the user-configured extra filesystem directories from the
2944
+ ha_mcp_tools component, plus the non-overridable deny floor for the UI
2945
+ blurb (issue #1567).
2946
+
2947
+ Always 200s with an ``available`` flag: when filesystem tools are off,
2948
+ the component is missing/too old, or HA is unreachable, ``available``
2949
+ is False with a human-readable ``reason`` so the UI can show a disabled
2950
+ section instead of an error.
2951
+ """
2952
+ from .tools.tools_filesystem import is_filesystem_tools_enabled
2953
+ from .tools.util_helpers import unwrap_service_response
2954
+
2955
+ def _unavailable(reason: str) -> JSONResponse:
2956
+ return JSONResponse(
2957
+ {
2958
+ "success": True,
2959
+ "available": False,
2960
+ "reason": reason,
2961
+ "paths": [],
2962
+ "deny_floor": [],
2963
+ }
2964
+ )
2965
+
2966
+ if not is_filesystem_tools_enabled():
2967
+ return _unavailable(
2968
+ "Filesystem tools are disabled. Enable them (beta) to "
2969
+ "configure custom directories."
2970
+ )
2971
+ try:
2972
+ result = await _fs_custom_paths_call("get_allowed_paths", {})
2973
+ except Exception as exc:
2974
+ logger.warning("fs-custom-paths GET could not reach ha_mcp_tools: %s", exc)
2975
+ return _unavailable(f"Could not reach the ha_mcp_tools component: {exc}")
2976
+
2977
+ data = unwrap_service_response(result) if isinstance(result, dict) else {}
2978
+ if not isinstance(data, dict) or not data.get("success", False):
2979
+ reason = (
2980
+ data.get("error") if isinstance(data, dict) else None
2981
+ ) or "ha_mcp_tools returned an unexpected response."
2982
+ return _unavailable(str(reason))
2983
+ return JSONResponse(
2984
+ {
2985
+ "success": True,
2986
+ "available": True,
2987
+ "paths": data.get("paths", []),
2988
+ "deny_floor": data.get("deny_floor", []),
2989
+ "builtin_read_dirs": data.get("builtin_read_dirs", []),
2990
+ "builtin_write_dirs": data.get("builtin_write_dirs", []),
2991
+ }
2992
+ )
2993
+
2994
+ async def _save_fs_custom_paths(request: Request) -> JSONResponse:
2995
+ """Replace the user-configured extra filesystem directories via the
2996
+ ha_mcp_tools component (issue #1567).
2997
+
2998
+ The component validates each entry and drops anything that hits the
2999
+ deny floor or escapes the config dir; the dropped entries come back in
3000
+ ``rejected``. ``restart_required`` is False — the component applies the
3001
+ new allowlist live.
3002
+ """
3003
+ from .tools.tools_filesystem import is_filesystem_tools_enabled
3004
+ from .tools.util_helpers import unwrap_service_response
3005
+
3006
+ if not is_filesystem_tools_enabled():
3007
+ return JSONResponse(
3008
+ create_error_response(
3009
+ ErrorCode.VALIDATION_INVALID_PARAMETER,
3010
+ "Filesystem tools are disabled; enable them (beta) before "
3011
+ "configuring custom directories.",
3012
+ ),
3013
+ status_code=409,
3014
+ )
3015
+ try:
3016
+ body = await request.json()
3017
+ except (ValueError, TypeError):
3018
+ return JSONResponse(
3019
+ create_error_response(
3020
+ ErrorCode.VALIDATION_INVALID_JSON,
3021
+ "Request body must be valid JSON.",
3022
+ ),
3023
+ status_code=400,
3024
+ )
3025
+ paths = body.get("paths") if isinstance(body, dict) else None
3026
+ if paths is None:
3027
+ paths = []
3028
+ if not isinstance(paths, list) or not all(isinstance(p, str) for p in paths):
3029
+ return JSONResponse(
3030
+ create_error_response(
3031
+ ErrorCode.VALIDATION_INVALID_PARAMETER,
3032
+ "'paths' must be a list of directory strings.",
3033
+ ),
3034
+ status_code=400,
3035
+ )
3036
+ try:
3037
+ result = await _fs_custom_paths_call("set_allowed_paths", {"paths": paths})
3038
+ except Exception as exc:
3039
+ logger.warning("fs-custom-paths POST could not reach ha_mcp_tools: %s", exc)
3040
+ return JSONResponse(
3041
+ create_error_response(
3042
+ ErrorCode.SERVICE_CALL_FAILED,
3043
+ f"Could not reach the ha_mcp_tools component: {exc}",
3044
+ ),
3045
+ status_code=502,
3046
+ )
3047
+
3048
+ data = unwrap_service_response(result) if isinstance(result, dict) else {}
3049
+ if not isinstance(data, dict) or not data.get("success", False):
3050
+ reason = (
3051
+ data.get("error") if isinstance(data, dict) else None
3052
+ ) or "ha_mcp_tools rejected the update."
3053
+ return JSONResponse(
3054
+ create_error_response(ErrorCode.SERVICE_CALL_FAILED, str(reason)),
3055
+ status_code=502,
3056
+ )
3057
+ return JSONResponse(
3058
+ {
3059
+ "success": True,
3060
+ "applied": data.get("paths", []),
3061
+ "paths": data.get("paths", []),
3062
+ "rejected": data.get("rejected", []),
3063
+ "mode": "component",
3064
+ "restart_required": False,
3065
+ }
3066
+ )
3067
+
2915
3068
  handlers: dict[str, Any] = {
2916
3069
  "root_page": _root_page,
2917
3070
  "settings_page": _settings_page,
@@ -2931,6 +3084,8 @@ def build_settings_handlers(
2931
3084
  "delete_backups_bulk": _delete_backups_bulk,
2932
3085
  "get_backup_config": _get_backup_config,
2933
3086
  "save_backup_config": _save_backup_config,
3087
+ "get_fs_custom_paths": _get_fs_custom_paths,
3088
+ "save_fs_custom_paths": _save_fs_custom_paths,
2934
3089
  }
2935
3090
 
2936
3091
  # Tool security policies. The main server attaches an
@@ -3100,6 +3255,9 @@ def register_settings_routes(
3100
3255
  ("/api/settings/backups/{name}", ["DELETE"], "delete_backup"),
3101
3256
  ("/api/settings/backup-config", ["GET"], "get_backup_config"),
3102
3257
  ("/api/settings/backup-config", ["POST"], "save_backup_config"),
3258
+ # Custom filesystem directories (issue #1567) — component-owned list
3259
+ ("/api/settings/fs-custom-paths", ["GET"], "get_fs_custom_paths"),
3260
+ ("/api/settings/fs-custom-paths", ["POST"], "save_fs_custom_paths"),
3103
3261
  # Tool security policies endpoints
3104
3262
  ("/api/policy/config", ["GET"], "policy_get_config"),
3105
3263
  ("/api/policy/config", ["PUT"], "policy_put_config"),
@@ -602,6 +602,21 @@ def _build_app(
602
602
  handlers["save_feature_flags"],
603
603
  methods=["POST"],
604
604
  ),
605
+ # Custom filesystem directories (issue #1567). The sub-form is rendered
606
+ # in the features panel, so the stdio sidecar must serve these too — its
607
+ # route list is hand-maintained and does NOT derive from
608
+ # register_settings_routes. The handler builds a transient HA client
609
+ # from the inherited env when server is None (sidecar mode).
610
+ Route(
611
+ f"{secret_prefix}/api/settings/fs-custom-paths",
612
+ handlers["get_fs_custom_paths"],
613
+ methods=["GET"],
614
+ ),
615
+ Route(
616
+ f"{secret_prefix}/api/settings/fs-custom-paths",
617
+ handlers["save_fs_custom_paths"],
618
+ methods=["POST"],
619
+ ),
605
620
  # Tool security policies endpoints (#966). Pending/approve/deny
606
621
  # are wired as stubs that return 503 in sidecar mode — the
607
622
  # in-memory ApprovalQueue lives in the main server process, so
@@ -53,7 +53,7 @@ CALLER_TOKEN_BOOTSTRAP_SERVICE = "get_caller_token"
53
53
  # server-side behavior change requires it. Older components (no
54
54
  # ``version`` in the get_caller_token response, or a version below this)
55
55
  # get an actionable "update via HACS" error.
56
- MIN_COMPONENT_VERSION = "0.5.2"
56
+ MIN_COMPONENT_VERSION = "0.7.0"
57
57
 
58
58
 
59
59
  def _version_tuple(version: str) -> tuple[int, ...]:
@@ -265,25 +265,6 @@ def _reset_caller_token_cache() -> None:
265
265
  _CALLER_TOKEN_LOCKS.clear()
266
266
 
267
267
 
268
- # Security constants - mirrors the custom component config
269
- READABLE_PATTERNS = [
270
- "configuration.yaml",
271
- "automations.yaml",
272
- "scripts.yaml",
273
- "scenes.yaml",
274
- "secrets.yaml", # Content will be masked by the custom component
275
- "packages/*.yaml",
276
- "home-assistant.log",
277
- "www/**",
278
- "themes/**",
279
- "custom_templates/**",
280
- "dashboards/**",
281
- "custom_components/**/*.py",
282
- ]
283
-
284
- WRITABLE_DIRS = ["www", "themes", "custom_templates", "dashboards"]
285
-
286
-
287
268
  def is_filesystem_tools_enabled() -> bool:
288
269
  """Check if the filesystem tools feature is enabled.
289
270
 
@@ -375,6 +356,7 @@ class FilesystemTools:
375
356
  - `themes/` - Theme files
376
357
  - `custom_templates/` - Jinja2 template files
377
358
  - `dashboards/` - YAML-mode dashboard files
359
+ - Plus any custom directories configured in the ha-mcp settings UI
378
360
 
379
361
  **Security:** Only directories in the allowed list can be accessed.
380
362
  Path traversal attempts (../) are blocked.
@@ -482,6 +464,7 @@ class FilesystemTools:
482
464
  - `home-assistant.log` (tail only)
483
465
  - `www/**`, `themes/**`, `custom_templates/**`, `dashboards/**`
484
466
  - `custom_components/**/*.py` (read-only)
467
+ - Plus any custom directories configured in the ha-mcp settings UI
485
468
 
486
469
  **Security:**
487
470
  - Path traversal (../) is blocked
@@ -604,6 +587,7 @@ class FilesystemTools:
604
587
  - `themes/` - Theme YAML files
605
588
  - `custom_templates/` - Jinja2 template files
606
589
  - `dashboards/` - YAML-mode dashboard files
590
+ - Plus any custom directories configured in the ha-mcp settings UI
607
591
 
608
592
  **Security:**
609
593
  - Only the directories above allow writes
@@ -718,6 +702,7 @@ class FilesystemTools:
718
702
  - `themes/` - Theme files
719
703
  - `custom_templates/` - Template files
720
704
  - `dashboards/` - YAML-mode dashboard files
705
+ - Plus any custom directories configured in the ha-mcp settings UI
721
706
 
722
707
  **Security:**
723
708
  - Only the directories above allow deletions
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev676
3
+ Version: 7.6.0.dev677
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
@@ -372,7 +372,7 @@ For comprehensive testing documentation, see **[tests/README.md](tests/README.md
372
372
 
373
373
  Ha-mcp runs **locally** on your machine. Your smart home data stays on your network.
374
374
 
375
- - **No telemetry today** — anonymous usage stats are a planned future feature (as of May 2026); users will be notified when it lands and it will be opt-in only
375
+ - **No telemetry today** — anonymous usage stats are a planned future feature (as of June 2026); when it lands it will follow your Home Assistant analytics/telemetry setting (which you can override), announced prominently in the release notes and the web Settings UI at least one month beforehand
376
376
  - **No personal data collection** — we never collect entity names, configs, or device data
377
377
  - **User-controlled bug reports** — only sent with your explicit approval
378
378
 
@@ -435,13 +435,15 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
435
435
  - **[@griffinmartin](https://github.com/griffinmartin)** — Added OpenCode (by Anomaly) as a selectable AI client in the setup wizard, with both stdio and streamable HTTP support.
436
436
  - **[@hhopke](https://github.com/hhopke)** — Fixed addon API calls to route through HA Core ingress proxy instead of direct container connections, fixing `ha_manage_addon` proxy mode on addon installs.
437
437
  - **[@tomwilkie](https://github.com/tomwilkie)** — JMESPath middleware exploration (#1147) whose review-time token-measurement data informed the design of #1199 and #1225.
438
- - **[@SealKan](https://github.com/SealKan)** — `fields=`/`attribute_keys=` projection on six read-heavy tools (#1225), `ha_call_event` tool (#1239), and dashboards-list helper refactor (#1207).
438
+ - **[@SealKan](https://github.com/SealKan)** — `fields=`/`attribute_keys=` projection on six read-heavy tools (#1225), `ha_call_event` tool (#1239), dashboards-list helper refactor (#1207), `for:`-field duration-math detector in the best-practice checker (#1264), persistent DCR OAuth client registrations across restarts (#1265), and issue-triage prompt token-budgeting (#1522).
439
439
  - **[@KarelTestSpecial](https://github.com/KarelTestSpecial)** — Cached YAML instance to prevent CPU spikes during bulk edits (#1371).
440
440
  - **[@corgan2222](https://github.com/corgan2222)** — HA brand assets for custom integration (#1317).
441
441
  - **[@drseanwing](https://github.com/drseanwing)** — Progress emission via FastMCP `Context` in long-running tools (#1124); tool-discovery / categorized-search docs (#1123).
442
442
  - **[@fnordpig](https://github.com/fnordpig)** — Config subentry support (#1393) and Assist pipeline management tool (#1392).
443
443
  - **[@paul43210](https://github.com/paul43210)** — `array_patch` mode in `ha_manage_addon` for atomic GET-modify-POST (#1063).
444
444
  - **[@L1AD](https://github.com/L1AD)** — Filed #966 proposing tool security policies; pointed to PolicyLayer's MCP-security work as prior art that inspired the predicate DSL shape.
445
+ - **[@nightcityblade](https://github.com/nightcityblade)** — Updated stale Home Assistant Advanced Mode references after HA 2026.6 made formerly advanced options available by default (#1533).
446
+ - **[@emmelutzer](https://github.com/emmelutzer)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
445
447
 
446
448
  ---
447
449