ha-mcp-dev 7.6.0.dev661__tar.gz → 7.6.0.dev663__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.dev661/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev663}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/__main__.py +86 -1
  4. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/LICENSE +0 -0
  6. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/README.md +0 -0
  8. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/_pypi_marker +0 -0
  11. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/_version.py +0 -0
  12. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/auth/__init__.py +0 -0
  13. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/auth/consent_form.py +0 -0
  14. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/auth/provider.py +0 -0
  15. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/backup_manager.py +0 -0
  16. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/client/__init__.py +0 -0
  17. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/client/rest_client.py +0 -0
  18. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/client/supervisor_client.py +0 -0
  19. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/client/websocket_client.py +0 -0
  20. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/client/websocket_listener.py +0 -0
  21. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/config.py +0 -0
  22. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  23. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  24. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  25. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/errors.py +0 -0
  26. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/__init__.py +0 -0
  27. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/approval_queue.py +0 -0
  28. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/evaluator.py +0 -0
  29. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/handlers.py +0 -0
  30. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/middleware.py +0 -0
  31. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/model.py +0 -0
  32. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/persistence.py +0 -0
  33. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/policy/value_sources.py +0 -0
  34. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/py.typed +0 -0
  35. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  36. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  37. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  38. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  39. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  40. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  41. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  42. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  43. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  44. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  45. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  46. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  47. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  48. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  49. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  50. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  51. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  52. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  53. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  54. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  55. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  56. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  57. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/server.py +0 -0
  58. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/settings.css +0 -0
  59. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/settings.js +0 -0
  60. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/settings_ui.py +0 -0
  61. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/smoke_test.py +0 -0
  62. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  63. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/__init__.py +0 -0
  64. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/auto_backup.py +0 -0
  65. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/backup.py +0 -0
  66. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  67. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/device_control.py +0 -0
  68. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/enhanced.py +0 -0
  69. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/helpers.py +0 -0
  70. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/reference_validator.py +0 -0
  71. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/registry.py +0 -0
  72. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  73. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  80. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  81. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_addons.py +0 -0
  82. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_areas.py +0 -0
  83. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  84. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  85. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_calendar.py +0 -0
  86. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_camera.py +0 -0
  87. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_categories.py +0 -0
  88. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_code.py +0 -0
  89. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  90. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  91. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  92. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  93. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  94. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  95. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  96. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_energy.py +0 -0
  97. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_entities.py +0 -0
  98. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  99. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_groups.py +0 -0
  100. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_hacs.py +0 -0
  101. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_history.py +0 -0
  102. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_integrations.py +0 -0
  103. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_labels.py +0 -0
  104. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_registry.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_resources.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_search.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_service.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_services.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_system.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_todo.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_traces.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_updates.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_utility.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/tools_zones.py +0 -0
  118. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/util_helpers.py +0 -0
  119. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/tools/validation_middleware.py +0 -0
  120. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/transforms/__init__.py +0 -0
  121. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/transforms/categorized_search.py +0 -0
  122. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/__init__.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/config_hash.py +0 -0
  125. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/data_paths.py +0 -0
  126. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/domain_handlers.py +0 -0
  127. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  128. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  129. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/operation_manager.py +0 -0
  130. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/python_sandbox.py +0 -0
  131. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/skill_loader.py +0 -0
  132. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp/utils/usage_logger.py +0 -0
  133. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  134. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  135. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  136. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  137. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  138. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/tests/__init__.py +0 -0
  139. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/tests/test_constants.py +0 -0
  140. {ha_mcp_dev-7.6.0.dev661 → ha_mcp_dev-7.6.0.dev663}/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.dev661
3
+ Version: 7.6.0.dev663
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.6.0.dev661"
7
+ version = "7.6.0.dev663"
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"
@@ -22,6 +22,7 @@ truststore.inject_into_ssl()
22
22
  import asyncio # noqa: E402
23
23
  import copy # noqa: E402
24
24
  import hashlib # noqa: E402
25
+ import ipaddress # noqa: E402
25
26
  import logging # noqa: E402
26
27
  import os # noqa: E402
27
28
  import signal # noqa: E402
@@ -755,10 +756,93 @@ def _get_http_runtime(default_port: int = 8086) -> tuple[str, int, str]:
755
756
  except ValueError:
756
757
  logger.error(f"Invalid MCP_PORT value: {port_str!r}. Must be an integer.")
757
758
  sys.exit(1)
758
- path = os.getenv("MCP_SECRET_PATH", "/mcp")
759
+ path = os.getenv("MCP_SECRET_PATH", DEFAULT_MCP_PATH)
759
760
  return host, port, path
760
761
 
761
762
 
763
+ # Default ``MCP_SECRET_PATH`` value, shared by ``_get_http_runtime`` (the
764
+ # read-from-env fallback) and ``_warn_if_default_path_exposed`` (the
765
+ # hardening-nudge predicate). Single source of truth so the two sites
766
+ # can't drift.
767
+ DEFAULT_MCP_PATH = "/mcp"
768
+
769
+ # Hostname literals (not IP addresses) treated as loopback by
770
+ # ``_is_loopback_host``. IP literals — the whole ``127.0.0.0/8`` block,
771
+ # ``::1``, bracketed forms, zone-suffixed forms, and IPv4-mapped IPv6 — are
772
+ # handled by the ``ipaddress`` parse before this set is consulted.
773
+ _LOOPBACK_HOSTNAMES = frozenset({"localhost", "ip6-localhost", "ip6-loopback"})
774
+
775
+
776
+ def _is_loopback_host(host: str) -> bool:
777
+ """Return True when ``host`` names the local machine only.
778
+
779
+ Accepts IPv6 hosts in bracketed (``[::1]``) or zone-suffixed
780
+ (``::1%eth0``) form, the full ``127.0.0.0/8`` range, IPv4-mapped IPv6
781
+ loopback (``::ffff:127.0.0.1``, which ``is_loopback`` resolves on its
782
+ own), and the names in ``_LOOPBACK_HOSTNAMES``. A value that is neither
783
+ an IP literal nor a known loopback name (a real hostname, or a malformed
784
+ string) is treated as non-loopback.
785
+ """
786
+ try:
787
+ candidate = host.strip("[]").split("%", 1)[0]
788
+ return ipaddress.ip_address(candidate).is_loopback
789
+ except ValueError:
790
+ # Not an IP literal — fall back to the known loopback hostnames.
791
+ return host.lower() in _LOOPBACK_HOSTNAMES
792
+
793
+
794
+ def _is_running_in_container() -> bool:
795
+ """Best-effort detection of containerized execution.
796
+
797
+ Inside a container the server binds ``0.0.0.0`` regardless of how the
798
+ operator restricted host-side exposure (``docker run -p 127.0.0.1:...``),
799
+ so the bind host alone can't tell a loopback-only deployment from a
800
+ LAN-reachable one — the default-path warning would be a false positive
801
+ for every container. Container deployments are hardened through the
802
+ published guidance instead (AGENTS.md -> Docker; the add-on
803
+ auto-generates a secret path).
804
+ """
805
+ # Docker writes /.dockerenv; Podman writes /run/.containerenv.
806
+ if os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv"):
807
+ return True
808
+ # The Home Assistant add-on runs under the Supervisor.
809
+ return bool(os.getenv("SUPERVISOR_TOKEN"))
810
+
811
+
812
+ def _warn_if_default_path_exposed(host: str, port: int, path: str) -> None:
813
+ """Warn on a direct run that leaves the default path on a LAN bind.
814
+
815
+ Standard-mode HTTP/SSE authenticates by URL-path secrecy (see
816
+ SECURITY.md → Threat Model). The default ``/mcp`` is not the
817
+ high-entropy secret that model assumes once the bind leaves loopback.
818
+
819
+ Fires only for a direct ``ha-mcp-web`` / ``ha-mcp-sse`` start (uvx, pip,
820
+ source) that uses the default path on a non-loopback host. Operators
821
+ silence it the same way they harden — bind ``MCP_HOST=127.0.0.1`` or set
822
+ a high-entropy ``MCP_SECRET_PATH``. Containers are skipped: an
823
+ in-container ``0.0.0.0`` bind says nothing about real exposure, which is
824
+ set by the ``docker -p`` mapping the process can't observe (see
825
+ ``_is_running_in_container``).
826
+ """
827
+ if path != DEFAULT_MCP_PATH:
828
+ return
829
+ if _is_running_in_container():
830
+ return
831
+ if _is_loopback_host(host):
832
+ return
833
+ logger.warning(
834
+ "ha-mcp listening on %s:%s%s with default MCP_SECRET_PATH. "
835
+ "Standard-mode HTTP/SSE authenticates by URL-path secrecy and assumes "
836
+ "a high-entropy MCP_SECRET_PATH for non-loopback binds "
837
+ "(see SECURITY.md → Threat Model). "
838
+ "Either bind loopback (MCP_HOST=127.0.0.1) or set MCP_SECRET_PATH "
839
+ "to a high-entropy value (e.g. /private_<token_urlsafe(16)>).",
840
+ host,
841
+ port,
842
+ path,
843
+ )
844
+
845
+
762
846
  async def _run_http_with_graceful_shutdown(
763
847
  transport: str,
764
848
  host: str,
@@ -861,6 +945,7 @@ def _run_http_server(transport: str, default_port: int = 8086) -> None:
861
945
  from ha_mcp.settings_ui import register_settings_routes
862
946
 
863
947
  host, port, path = _get_http_runtime(default_port)
948
+ _warn_if_default_path_exposed(host, port, path)
864
949
  register_browser_landing(_get_mcp(), path)
865
950
  register_settings_routes(_get_mcp(), _get_server(), secret_path=path)
866
951
  _log_settings_url(host, port, path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev661
3
+ Version: 7.6.0.dev663
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