ha-mcp-dev 7.6.0.dev644__tar.gz → 7.6.0.dev645__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 (136) hide show
  1. {ha_mcp_dev-7.6.0.dev644/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev645}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_system.py +125 -57
  4. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/LICENSE +0 -0
  6. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/README.md +0 -0
  8. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/_version.py +0 -0
  13. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/auth/__init__.py +0 -0
  14. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/auth/consent_form.py +0 -0
  15. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/auth/provider.py +0 -0
  16. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/backup_manager.py +0 -0
  17. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/client/__init__.py +0 -0
  18. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/client/rest_client.py +0 -0
  19. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/client/supervisor_client.py +0 -0
  20. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/client/websocket_client.py +0 -0
  21. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/client/websocket_listener.py +0 -0
  22. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/config.py +0 -0
  23. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/errors.py +0 -0
  24. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/__init__.py +0 -0
  25. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/approval_queue.py +0 -0
  26. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/evaluator.py +0 -0
  27. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/handlers.py +0 -0
  28. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/middleware.py +0 -0
  29. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/model.py +0 -0
  30. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/persistence.py +0 -0
  31. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/policy/value_sources.py +0 -0
  32. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/py.typed +0 -0
  33. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  34. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  35. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  36. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  37. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  38. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  39. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  40. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  41. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  42. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  43. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  44. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  45. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  46. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  47. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  48. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  49. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  50. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  51. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  52. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  53. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  54. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  55. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/server.py +0 -0
  56. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/settings.css +0 -0
  57. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/settings.js +0 -0
  58. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/settings_ui.py +0 -0
  59. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/smoke_test.py +0 -0
  60. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  61. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/__init__.py +0 -0
  62. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/auto_backup.py +0 -0
  63. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/backup.py +0 -0
  64. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  65. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/device_control.py +0 -0
  66. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/enhanced.py +0 -0
  67. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/helpers.py +0 -0
  68. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/reference_validator.py +0 -0
  69. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/registry.py +0 -0
  70. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  71. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  72. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  73. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_addons.py +0 -0
  80. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_areas.py +0 -0
  81. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  82. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  83. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_calendar.py +0 -0
  84. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_camera.py +0 -0
  85. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_categories.py +0 -0
  86. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_code.py +0 -0
  87. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  88. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  89. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  90. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  91. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  92. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  93. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_energy.py +0 -0
  94. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_entities.py +0 -0
  95. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  96. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_groups.py +0 -0
  97. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_hacs.py +0 -0
  98. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_history.py +0 -0
  99. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_integrations.py +0 -0
  100. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_labels.py +0 -0
  101. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  102. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_registry.py +0 -0
  103. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_resources.py +0 -0
  104. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_search.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_service.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_services.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_todo.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_traces.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_updates.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_utility.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/tools_zones.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/util_helpers.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/tools/validation_middleware.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/transforms/__init__.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/transforms/categorized_search.py +0 -0
  118. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  119. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/__init__.py +0 -0
  120. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/config_hash.py +0 -0
  121. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/data_paths.py +0 -0
  122. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/domain_handlers.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  125. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/operation_manager.py +0 -0
  126. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/python_sandbox.py +0 -0
  127. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/skill_loader.py +0 -0
  128. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp/utils/usage_logger.py +0 -0
  129. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  130. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  131. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  132. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  133. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  134. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/tests/__init__.py +0 -0
  135. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/tests/test_constants.py +0 -0
  136. {ha_mcp_dev-7.6.0.dev644 → ha_mcp_dev-7.6.0.dev645}/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.dev644
3
+ Version: 7.6.0.dev645
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.dev644"
7
+ version = "7.6.0.dev645"
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"
@@ -60,51 +60,6 @@ class SystemTools:
60
60
  def __init__(self, client: Any) -> None:
61
61
  self._client = client
62
62
 
63
- @tool(
64
- name="ha_check_config",
65
- tags={"System"},
66
- annotations={
67
- "idempotentHint": True,
68
- "readOnlyHint": True,
69
- "title": "Check Configuration",
70
- },
71
- )
72
- @log_tool_usage
73
- async def ha_check_config(self) -> dict[str, Any]:
74
- """
75
- Check Home Assistant configuration for errors.
76
-
77
- Validates configuration files without applying changes.
78
- Always run this before ha_restart() to ensure configuration is valid.
79
- """
80
- try:
81
- config_result = await self._client.check_config()
82
-
83
- # The API returns {"result": "valid"} or {"result": "invalid", "errors": [...]}
84
- is_valid = config_result.get("result") == "valid"
85
- errors = config_result.get("errors") or [] # Handle None case
86
-
87
- return {
88
- "success": True,
89
- "result": "valid" if is_valid else "invalid",
90
- "is_valid": is_valid,
91
- "errors": errors,
92
- "message": (
93
- "Configuration is valid"
94
- if is_valid
95
- else f"Configuration has {len(errors)} error(s)"
96
- ),
97
- }
98
-
99
- except Exception as e:
100
- exception_to_structured_error(
101
- e,
102
- suggestions=[
103
- "Ensure Home Assistant is running and accessible",
104
- "Check your connection settings",
105
- ],
106
- )
107
-
108
63
  @tool(
109
64
  name="ha_restart",
110
65
  tags={"System"},
@@ -127,15 +82,16 @@ class SystemTools:
127
82
  measure to prevent accidental restarts.
128
83
 
129
84
  **Best Practices:**
130
- 1. Always run ha_check_config() first to ensure configuration is valid
85
+ 1. Config is validated automatically before the restart proceeds; to
86
+ pre-check, call ha_get_system_health(include="config_check")
131
87
  2. Notify users before restarting (if applicable)
132
88
  3. Schedule restarts during low-activity periods
133
89
 
134
90
  **Example Usage:**
135
91
  ```python
136
- # Always check config first
137
- config = ha_check_config()
138
- if config["result"] == "valid":
92
+ # Optional pre-check (ha_restart also validates config automatically)
93
+ health = ha_get_system_health(include="config_check")
94
+ if health["config_check"]["is_valid"]:
139
95
  # Restart with confirmation
140
96
  result = ha_restart(confirm=True)
141
97
  ```
@@ -153,7 +109,7 @@ class SystemTools:
153
109
  "This is a safety measure to prevent accidental restarts."
154
110
  ),
155
111
  suggestions=[
156
- "Run ha_check_config() first to validate configuration",
112
+ 'Pre-check config via ha_get_system_health(include="config_check")',
157
113
  "Call ha_restart(confirm=True) to proceed with restart",
158
114
  "Consider using ha_reload_core() for config-only changes",
159
115
  ],
@@ -391,7 +347,10 @@ class SystemTools:
391
347
  be large (Hue ~290 KB, ZHA/MQTT/ESPHome several MB) — pair with
392
348
  ``diagnostics_fields`` or ``diagnostics_truncate_at_bytes`` to fit the LLM
393
349
  context budget.
394
- - Example: include="repairs,zha_network,zwave_network"
350
+ - "config_check": Validate HA configuration via POST /config/core/check_config
351
+ (the pre-restart safety check; ha_restart runs it automatically). Returns
352
+ {result: valid|invalid, is_valid, errors}; read-only/idempotent, takes no args.
353
+ - Example: include="repairs,zha_network,zwave_network,config_check"
395
354
  - Example: include="diagnostics", config_entry_id="abc123..."
396
355
  - include_dismissed_repairs: Include user-dismissed/ignored repairs (default: False). Only meaningful when "repairs" is in `include`.
397
356
  - config_entry_id: Required when ``include`` contains ``diagnostics``. The config
@@ -430,10 +389,47 @@ class SystemTools:
430
389
  includes = self._parse_includes(include)
431
390
  include_dismissed_repairs_bool = bool(include_dismissed_repairs)
432
391
 
392
+ # Sections that require the system_health WebSocket connection; the
393
+ # REST-based sections (config_check, diagnostics) do not.
394
+ ws_backed = {"repairs", "zha_network", "zha_network_full", "zwave_network"}
395
+
433
396
  ws_client = None
434
397
 
435
398
  try:
436
- ws_client, result = await self._fetch_health_info()
399
+ try:
400
+ ws_client, result = await self._fetch_health_info()
401
+ except ToolError as health_err:
402
+ # The system_health/info baseline (WebSocket) is unavailable.
403
+ # Only ``await self._fetch_health_info()`` runs in this inner
404
+ # try, and it raises ToolError solely for baseline-unavailable
405
+ # conditions (connect failure / timeout / WS error), so this
406
+ # catch cannot swallow an unrelated ToolError.
407
+ #
408
+ # Degrade gracefully ONLY when the caller asked for a REST-based
409
+ # section (config_check / diagnostics) that can still be served
410
+ # without the WebSocket. config_check is the pure-REST
411
+ # replacement for the removed ha_check_config tool, so it must
412
+ # not depend on the health WebSocket (the system_health/info
413
+ # command carries its own 10s timeout and can hang/be absent on
414
+ # some installs). If the caller asked for nothing (the health
415
+ # baseline itself) or only WS-backed sections, the baseline WAS
416
+ # the deliverable: re-raise so the failure surfaces as
417
+ # isError=true, exactly as before this change.
418
+ if not (includes & {"config_check", "diagnostics"}):
419
+ raise
420
+ logger.warning("system_health baseline unavailable: %s", health_err)
421
+ ws_client = None
422
+ result = {
423
+ "success": True,
424
+ "baseline_available": False,
425
+ "health_info": {},
426
+ "component_count": 0,
427
+ "message": "System health baseline unavailable.",
428
+ "warnings": [
429
+ "system_health baseline unavailable; "
430
+ "returning REST-based sections only."
431
+ ],
432
+ }
437
433
 
438
434
  # Warn about unrecognized include values
439
435
  VALID_INCLUDES = {
@@ -442,6 +438,7 @@ class SystemTools:
442
438
  "zha_network_full",
443
439
  "zwave_network",
444
440
  "diagnostics",
441
+ "config_check",
445
442
  }
446
443
  unknown = includes - VALID_INCLUDES
447
444
  if unknown:
@@ -464,6 +461,27 @@ class SystemTools:
464
461
  want_zha = zha_full or zha_summary
465
462
  want_zwave = "zwave_network" in includes
466
463
 
464
+ if ws_client is None:
465
+ # Health WebSocket unavailable: WS-backed sections can't run.
466
+ # Give each requested WS-backed section a machine-readable error
467
+ # sub-dict under its own key (same shape the section carries when
468
+ # the baseline is up but the fetch fails), plus a summary
469
+ # warning, then skip them so the REST sections below still run.
470
+ ws_error = "requires the system_health WebSocket, which is unavailable"
471
+ if want_repairs:
472
+ result["repairs"] = {"error": ws_error}
473
+ if want_zha:
474
+ result["zha_network"] = {"error": ws_error}
475
+ if want_zwave:
476
+ result["zwave_network"] = {"error": ws_error}
477
+ unavailable = sorted(includes & ws_backed)
478
+ if unavailable:
479
+ result.setdefault("warnings", []).append(
480
+ "These sections require the system_health WebSocket, "
481
+ f"which is unavailable: {', '.join(unavailable)}"
482
+ )
483
+ want_repairs = want_zha = want_zwave = False
484
+
467
485
  sections: list[tuple[str, Coroutine[Any, Any, dict[str, Any]]]] = []
468
486
  if want_repairs:
469
487
  sections.append(
@@ -593,6 +611,14 @@ class SystemTools:
593
611
  "in include"
594
612
  )
595
613
 
614
+ if "config_check" in includes:
615
+ # REST call on self._client (POST /config/core/check_config),
616
+ # not a ws_client command — so it runs inline like diagnostics
617
+ # rather than in the ws ``sections`` gather above. Standalone
618
+ # ``if`` (not chained to diagnostics) so both can be requested
619
+ # in one call.
620
+ result["config_check"] = await self._fetch_config_check()
621
+
596
622
  return result
597
623
 
598
624
  except ToolError:
@@ -606,11 +632,7 @@ class SystemTools:
606
632
  ],
607
633
  )
608
634
  finally:
609
- if ws_client:
610
- try:
611
- await ws_client.disconnect()
612
- except Exception:
613
- pass
635
+ await self._safe_disconnect(ws_client)
614
636
 
615
637
  @staticmethod
616
638
  def _parse_includes(include: str | None) -> set[str]:
@@ -619,6 +641,16 @@ class SystemTools:
619
641
  return set()
620
642
  return {s.strip().lower() for s in include.split(",") if s.strip()}
621
643
 
644
+ @staticmethod
645
+ async def _safe_disconnect(ws_client: Any) -> None:
646
+ """Best-effort WebSocket disconnect; never raises."""
647
+ if ws_client is None:
648
+ return
649
+ try:
650
+ await ws_client.disconnect()
651
+ except Exception:
652
+ pass
653
+
622
654
  async def _fetch_health_info(self) -> tuple[Any, dict[str, Any]]:
623
655
  """Connect to WebSocket and retrieve system health info.
624
656
 
@@ -645,6 +677,9 @@ class SystemTools:
645
677
  "system_health/info", wait_timeout=10.0
646
678
  )
647
679
  except TimeoutError:
680
+ # The connection opened but the command stalled — disconnect it
681
+ # before raising so we don't leak the socket.
682
+ await self._safe_disconnect(ws_client)
648
683
  raise_tool_error(
649
684
  create_error_response(
650
685
  ErrorCode.SERVICE_CALL_FAILED,
@@ -652,6 +687,7 @@ class SystemTools:
652
687
  )
653
688
  )
654
689
  except Exception as e:
690
+ await self._safe_disconnect(ws_client)
655
691
  raise_tool_error(
656
692
  create_error_response(
657
693
  ErrorCode.SERVICE_CALL_FAILED,
@@ -818,6 +854,38 @@ class SystemTools:
818
854
  )
819
855
  return zwave_network
820
856
 
857
+ async def _fetch_config_check(self) -> dict[str, Any]:
858
+ """Validate HA configuration via POST /config/core/check_config.
859
+
860
+ Returns an embeddable sub-dict (matching the ``_fetch_repairs`` /
861
+ ``_fetch_zha_network`` convention): baseline keys always present, with
862
+ an ``error`` field on backend failure. Never raises, so a config-check
863
+ failure surfaces as ``result["config_check"]["error"]`` without sinking
864
+ the rest of ha_get_system_health. Instance method (not @staticmethod)
865
+ because it calls the REST client (``self._client``), like the
866
+ diagnostics path.
867
+ """
868
+ config_check: dict[str, Any] = {
869
+ "result": "unknown",
870
+ "is_valid": False,
871
+ "errors": [],
872
+ }
873
+ try:
874
+ config_result = await self._client.check_config()
875
+ # The API returns {"result": "valid"} or
876
+ # {"result": "invalid", "errors": [...]}.
877
+ is_valid = config_result.get("result") == "valid"
878
+ errors = config_result.get("errors") or [] # Handle None case
879
+ config_check = {
880
+ "result": "valid" if is_valid else "invalid",
881
+ "is_valid": is_valid,
882
+ "errors": errors,
883
+ }
884
+ except Exception as e:
885
+ logger.warning("Failed to check config: %s", e)
886
+ config_check["error"] = f"Config check not available: {e}"
887
+ return config_check
888
+
821
889
 
822
890
  def register_system_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
823
891
  """Register Home Assistant system management tools."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev644
3
+ Version: 7.6.0.dev645
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