ha-mcp-dev 7.7.0.dev698__tar.gz → 7.8.0.dev701__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 (143) hide show
  1. {ha_mcp_dev-7.7.0.dev698/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.8.0.dev701}/PKG-INFO +2 -2
  2. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/pyproject.toml +2 -2
  3. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/__main__.py +37 -7
  4. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/client/rest_client.py +56 -31
  5. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_hacs.py +8 -6
  6. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
  7. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp_dev.egg-info/requires.txt +1 -1
  8. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/LICENSE +0 -0
  9. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/MANIFEST.in +0 -0
  10. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/README.md +0 -0
  11. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/setup.cfg +0 -0
  12. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/__init__.py +0 -0
  13. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/_pypi_marker +0 -0
  14. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/_version.py +0 -0
  15. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/auth/__init__.py +0 -0
  16. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/auth/consent_form.py +0 -0
  17. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/auth/provider.py +0 -0
  18. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/backup_manager.py +0 -0
  19. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/client/__init__.py +0 -0
  20. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/client/supervisor_client.py +0 -0
  21. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/client/websocket_client.py +0 -0
  22. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/client/websocket_listener.py +0 -0
  23. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/config.py +0 -0
  24. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  25. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  26. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  27. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/errors.py +0 -0
  28. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/__init__.py +0 -0
  29. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/approval_queue.py +0 -0
  30. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/evaluator.py +0 -0
  31. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/handlers.py +0 -0
  32. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/middleware.py +0 -0
  33. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/model.py +0 -0
  34. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/persistence.py +0 -0
  35. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/policy/value_sources.py +0 -0
  36. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/py.typed +0 -0
  37. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/read_only.py +0 -0
  38. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  39. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  40. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  41. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  42. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  43. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  44. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  45. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  46. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  47. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  48. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  49. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  50. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  51. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  52. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  53. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  54. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  55. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  56. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  57. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  58. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  59. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  60. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/server.py +0 -0
  61. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/settings.css +0 -0
  62. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/settings.js +0 -0
  63. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/settings_ui.py +0 -0
  64. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/smoke_test.py +0 -0
  65. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  66. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/__init__.py +0 -0
  67. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/auto_backup.py +0 -0
  68. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/backup.py +0 -0
  69. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  70. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  71. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/device_control.py +0 -0
  72. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/enhanced.py +0 -0
  73. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/helpers.py +0 -0
  74. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/reference_validator.py +0 -0
  75. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/registry.py +0 -0
  76. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  77. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  78. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  79. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  80. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  81. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  82. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  83. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  84. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  85. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  86. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_addons.py +0 -0
  87. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_areas.py +0 -0
  88. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  89. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  90. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_calendar.py +0 -0
  91. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_camera.py +0 -0
  92. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_categories.py +0 -0
  93. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_code.py +0 -0
  94. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  95. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  96. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  97. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  98. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  99. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  100. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_energy.py +0 -0
  101. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_entities.py +0 -0
  102. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  103. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_groups.py +0 -0
  104. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_history.py +0 -0
  105. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_integrations.py +0 -0
  106. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_labels.py +0 -0
  107. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  108. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_registry.py +0 -0
  109. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_resources.py +0 -0
  110. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_search.py +0 -0
  111. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_service.py +0 -0
  112. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_services.py +0 -0
  113. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_system.py +0 -0
  114. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_themes.py +0 -0
  115. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_todo.py +0 -0
  116. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_traces.py +0 -0
  117. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_updates.py +0 -0
  118. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_utility.py +0 -0
  119. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  120. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  121. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/tools_zones.py +0 -0
  122. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/util_helpers.py +0 -0
  123. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/tools/validation_middleware.py +0 -0
  124. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/transforms/__init__.py +0 -0
  125. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/transforms/categorized_search.py +0 -0
  126. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  127. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/__init__.py +0 -0
  128. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/config_hash.py +0 -0
  129. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/data_paths.py +0 -0
  130. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/domain_handlers.py +0 -0
  131. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  132. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  133. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/operation_manager.py +0 -0
  134. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/python_sandbox.py +0 -0
  135. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/skill_loader.py +0 -0
  136. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp/utils/usage_logger.py +0 -0
  137. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  138. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  139. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  140. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  141. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/tests/__init__.py +0 -0
  142. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/tests/test_constants.py +0 -0
  143. {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.8.0.dev701}/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.7.0.dev698
3
+ Version: 7.8.0.dev701
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
@@ -24,7 +24,7 @@ Requires-Dist: pydantic==2.13.4
24
24
  Requires-Dist: python-dotenv==1.2.2
25
25
  Requires-Dist: truststore==0.10.4
26
26
  Requires-Dist: websockets==16.0
27
- Requires-Dist: cryptography==48.0.0
27
+ Requires-Dist: cryptography==49.0.0
28
28
  Requires-Dist: pydantic-monty==0.0.18
29
29
  Dynamic: license-file
30
30
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.7.0.dev698"
7
+ version = "7.8.0.dev701"
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"
@@ -30,7 +30,7 @@ dependencies = [
30
30
  "python-dotenv==1.2.2",
31
31
  "truststore==0.10.4",
32
32
  "websockets==16.0",
33
- "cryptography==48.0.0",
33
+ "cryptography==49.0.0",
34
34
  "pydantic-monty==0.0.18",
35
35
  ]
36
36
 
@@ -290,17 +290,28 @@ def _setup_standard_mode() -> None:
290
290
  _log_startup_version()
291
291
 
292
292
 
293
- def _http_run_kwargs(transport: str, host: str, port: int, path: str) -> dict:
294
- """Build common run_async kwargs for HTTP-based transports."""
295
- return {
293
+ def _http_run_kwargs(transport: str, host: str, port: int, path: str) -> dict[str, Any]:
294
+ """Build common run_async kwargs for HTTP-based transports.
295
+
296
+ ``stateless_http`` is a Streamable-HTTP concept and is only valid for the
297
+ ``http``/``streamable-http`` transports. Passing it alongside
298
+ ``transport="sse"`` makes fastmcp's ``run_async`` raise
299
+ ``ValueError("SSE transport does not support stateless mode")``. Gating it to
300
+ non-SSE transports keeps SSE startup working. (Before this fix that raise was
301
+ also swallowed into a silent exit 0; ``_run_with_shutdown`` now surfaces a
302
+ self-terminating server task's exception instead.) See #1544.
303
+ """
304
+ kwargs: dict[str, Any] = {
296
305
  "transport": transport,
297
306
  "host": host,
298
307
  "port": port,
299
308
  "path": path,
300
309
  "show_banner": _get_show_banner(),
301
- "stateless_http": True,
302
310
  "uvicorn_config": {"log_config": _get_timestamped_uvicorn_log_config()},
303
311
  }
312
+ if transport != "sse":
313
+ kwargs["stateless_http"] = True
314
+ return kwargs
304
315
 
305
316
 
306
317
  def _create_server() -> "HomeAssistantSmartMCPServer":
@@ -572,9 +583,23 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
572
583
  # Expected: we just cancelled server_task above; swallow its
573
584
  # CancelledError so shutdown can proceed to cleanup.
574
585
  pass
586
+ elif server_task in done:
587
+ # Server task finished on its own (no shutdown signal). Re-raise any
588
+ # exception it captured so a hard startup failure surfaces as a
589
+ # logged sys.exit(1) instead of a silent exit 0 — without this the
590
+ # exception on the already-done task is never retrieved. See #1544.
591
+ server_task.result()
575
592
 
576
593
  except asyncio.CancelledError:
577
- logger.info("Server task cancelled")
594
+ # A shutdown-initiated cancel is a graceful stop. A cancel without a
595
+ # shutdown signal — including one re-raised by server_task.result()
596
+ # above — is a hard stop masquerading as success; re-raise it so it
597
+ # becomes a logged sys.exit(1) rather than a silent exit 0. See #1544.
598
+ if _shutdown_event is not None and _shutdown_event.is_set():
599
+ logger.info("Server task cancelled")
600
+ else:
601
+ logger.error("Server task cancelled without a shutdown signal")
602
+ raise
578
603
  finally:
579
604
  try:
580
605
  await asyncio.wait_for(
@@ -583,7 +608,12 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
583
608
  except TimeoutError:
584
609
  logger.warning("Resource cleanup timed out")
585
610
 
586
- await _cancel_tasks(server_task, shutdown_task)
611
+ try:
612
+ await _cancel_tasks(server_task, shutdown_task)
613
+ except Exception as e:
614
+ # Teardown must never mask the exception being propagated from the
615
+ # try block (Python drops the original if finally raises).
616
+ logger.warning(f"Task cancellation during shutdown failed: {e}")
587
617
 
588
618
 
589
619
  def _run_entrypoint(coro: Coroutine[Any, Any, Any], label: str) -> None:
@@ -597,7 +627,7 @@ def _run_entrypoint(coro: Coroutine[Any, Any, Any], label: str) -> None:
597
627
  except SystemExit:
598
628
  raise
599
629
  except Exception as e:
600
- logger.error(f"{label} error: {e}")
630
+ logger.error(f"{label} error: {e}", exc_info=True)
601
631
  sys.exit(1)
602
632
 
603
633
  sys.exit(0)
@@ -33,6 +33,12 @@ def _is_ssl_error(exc: BaseException) -> bool:
33
33
 
34
34
  logger = logging.getLogger(__name__)
35
35
 
36
+ # Transient gateway statuses from a reverse proxy / Supervisor ingress — HA Core
37
+ # restarting or briefly overloaded behind it. The upstream couldn't be reached,
38
+ # so the request did not execute and retrying is safe even for writes.
39
+ _RETRYABLE_STATUS = frozenset({502, 503, 504})
40
+ _MAX_REQUEST_ATTEMPTS = 3
41
+
36
42
 
37
43
  class HomeAssistantError(Exception):
38
44
  """Base exception for Home Assistant API errors."""
@@ -180,7 +186,8 @@ class HomeAssistantClient:
180
186
 
181
187
  Handles auth, HTTP 4xx/5xx, and transport errors in one place.
182
188
  Callers parse the body themselves (JSON via `_request`, text via
183
- `get_addon_logs`, etc.).
189
+ `get_addon_logs`, etc.). Transient gateway errors (502/503/504) are
190
+ retried with bounded exponential backoff before surfacing.
184
191
 
185
192
  Raises:
186
193
  HomeAssistantAuthError: 401 response.
@@ -188,44 +195,62 @@ class HomeAssistantClient:
188
195
  response_data set from JSON body when possible).
189
196
  HomeAssistantConnectionError: Network, timeout, or transport error.
190
197
  """
191
- try:
192
- response = await self.httpx_client.request(method, endpoint, **kwargs)
198
+ backoff = 0.5
199
+ for attempt in range(1, _MAX_REQUEST_ATTEMPTS + 1):
200
+ try:
201
+ response = await self.httpx_client.request(method, endpoint, **kwargs)
193
202
 
194
- if response.status_code == 401:
195
- raise HomeAssistantAuthError("Invalid authentication token")
203
+ if response.status_code == 401:
204
+ raise HomeAssistantAuthError("Invalid authentication token")
196
205
 
197
- if response.status_code >= 400:
198
- try:
199
- error_data = response.json()
200
- except Exception:
201
- error_data = {"message": response.text}
206
+ if response.status_code >= 400:
207
+ try:
208
+ error_data = response.json()
209
+ except Exception:
210
+ error_data = {"message": response.text}
202
211
 
203
- message = error_data.get("message")
204
- if not message or not message.strip():
205
- message = response.reason_phrase or "<empty body>"
212
+ message = error_data.get("message")
213
+ if not message or not message.strip():
214
+ message = response.reason_phrase or "<empty body>"
206
215
 
207
- raise HomeAssistantAPIError(
208
- f"API error: {response.status_code} - {message}",
209
- status_code=response.status_code,
210
- response_data=error_data,
211
- )
216
+ if (
217
+ response.status_code in _RETRYABLE_STATUS
218
+ and attempt < _MAX_REQUEST_ATTEMPTS
219
+ ):
220
+ logger.warning(
221
+ f"Transient {response.status_code} from Home Assistant "
222
+ f"(attempt {attempt}/{_MAX_REQUEST_ATTEMPTS}), retrying "
223
+ f"in {backoff}s: {message}"
224
+ )
225
+ await asyncio.sleep(backoff)
226
+ backoff *= 2
227
+ continue
212
228
 
213
- return response
229
+ raise HomeAssistantAPIError(
230
+ f"API error: {response.status_code} - {message}",
231
+ status_code=response.status_code,
232
+ response_data=error_data,
233
+ )
214
234
 
215
- except httpx.ConnectError as e:
216
- if _is_ssl_error(e) and self.verify_ssl:
235
+ return response
236
+
237
+ except httpx.ConnectError as e:
238
+ if _is_ssl_error(e) and self.verify_ssl:
239
+ raise HomeAssistantConnectionError(
240
+ f"TLS verification failed for {self.base_url}: {e}. "
241
+ "If this is a self-signed certificate or hostname "
242
+ "mismatch, set HA_VERIFY_SSL=false to skip verification."
243
+ ) from e
217
244
  raise HomeAssistantConnectionError(
218
- f"TLS verification failed for {self.base_url}: {e}. "
219
- "If this is a self-signed certificate or hostname "
220
- "mismatch, set HA_VERIFY_SSL=false to skip verification."
245
+ f"Failed to connect to Home Assistant: {e}"
221
246
  ) from e
222
- raise HomeAssistantConnectionError(
223
- f"Failed to connect to Home Assistant: {e}"
224
- ) from e
225
- except httpx.TimeoutException as e:
226
- raise HomeAssistantConnectionError(f"Request timeout: {e}") from e
227
- except httpx.HTTPError as e:
228
- raise HomeAssistantConnectionError(f"HTTP error: {e}") from e
247
+ except httpx.TimeoutException as e:
248
+ raise HomeAssistantConnectionError(f"Request timeout: {e}") from e
249
+ except httpx.HTTPError as e:
250
+ raise HomeAssistantConnectionError(f"HTTP error: {e}") from e
251
+
252
+ # Unreachable: the final attempt takes the non-retry branch and raises.
253
+ raise AssertionError("_raw_request retry loop exhausted without returning")
229
254
 
230
255
  async def _request(
231
256
  self, method: str, endpoint: str, **kwargs: Any
@@ -674,12 +674,14 @@ def _filter_and_score_repos(
674
674
  HACS_REPOSITORY_SIGNAL = "hacs_dispatch_repository"
675
675
 
676
676
  # Wall-clock budget for ``wait_for_repo_registration``. Generous
677
- # because the constraint is "HACS finishes registration"; the prior
678
- # 10 s budget (10 attempts × 1.0 s) was exhausted on the HAOS E2E
679
- # channel under load, so a headroom backstop avoids re-tripping
680
- # the same flake. The subscription nudges us, so this is a wall-
681
- # clock cap rather than the dominant cost.
682
- HACS_REPO_REGISTRATION_TIMEOUT = 30.0
677
+ # because the constraint is "HACS finishes registration": adding a
678
+ # fresh repo makes HACS clone/index it over the network, which on a
679
+ # slow link (or a loaded HAOS E2E runner) can exceed 30 s — the prior
680
+ # value, which flaked ``test_install_mcp_tools_*`` with "Could not
681
+ # find repository ID after adding". The subscription nudges us the
682
+ # instant registration lands, so the happy path returns in seconds and
683
+ # this larger cap only ever costs wall-clock on a genuinely slow add.
684
+ HACS_REPO_REGISTRATION_TIMEOUT = 60.0
683
685
 
684
686
  # Budget for the initial ``hacs/subscribe`` ack. Smaller than the
685
687
  # overall registration timeout so a slow subscribe doesn't consume
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.7.0.dev698
3
+ Version: 7.8.0.dev701
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
@@ -24,7 +24,7 @@ Requires-Dist: pydantic==2.13.4
24
24
  Requires-Dist: python-dotenv==1.2.2
25
25
  Requires-Dist: truststore==0.10.4
26
26
  Requires-Dist: websockets==16.0
27
- Requires-Dist: cryptography==48.0.0
27
+ Requires-Dist: cryptography==49.0.0
28
28
  Requires-Dist: pydantic-monty==0.0.18
29
29
  Dynamic: license-file
30
30
 
@@ -4,5 +4,5 @@ pydantic==2.13.4
4
4
  python-dotenv==1.2.2
5
5
  truststore==0.10.4
6
6
  websockets==16.0
7
- cryptography==48.0.0
7
+ cryptography==49.0.0
8
8
  pydantic-monty==0.0.18