ha-mcp-dev 7.14.2.dev2109__tar.gz → 7.14.2.dev2113__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 (190) hide show
  1. {ha_mcp_dev-7.14.2.dev2109/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.14.2.dev2113}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/__main__.py +140 -19
  4. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/LICENSE +0 -0
  6. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/README.md +0 -0
  8. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/_pypi_marker +0 -0
  11. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/_version.py +0 -0
  12. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/auth/__init__.py +0 -0
  13. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/auth/consent_form.py +0 -0
  14. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/auth/provider.py +0 -0
  15. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/backup_manager.py +0 -0
  16. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/browser_landing.py +0 -0
  17. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/client/__init__.py +0 -0
  18. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/client/rest_client.py +0 -0
  19. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/client/supervisor_client.py +0 -0
  20. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/client/websocket_client.py +0 -0
  21. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/client/websocket_listener.py +0 -0
  22. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/config.py +0 -0
  23. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  24. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  25. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  26. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  27. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  28. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  29. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/errors.py +0 -0
  30. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/llm_exposure.py +0 -0
  31. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/__init__.py +0 -0
  32. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/approval_queue.py +0 -0
  33. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/evaluator.py +0 -0
  34. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/handlers.py +0 -0
  35. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/middleware.py +0 -0
  36. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/model.py +0 -0
  37. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/persistence.py +0 -0
  38. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/policy/value_sources.py +0 -0
  39. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/py.typed +0 -0
  40. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/read_only.py +0 -0
  41. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  42. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  43. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  44. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  45. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  46. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  47. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  48. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  49. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  50. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  51. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  52. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  53. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  54. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  55. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  56. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  57. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  58. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  59. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  60. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  61. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  62. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  63. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  64. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  65. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  66. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/server.py +0 -0
  67. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/__init__.py +0 -0
  68. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  69. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  70. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  71. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  72. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  73. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  74. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  75. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  76. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  77. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_theme.py +0 -0
  78. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  79. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  80. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  81. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/locales/en.json +0 -0
  82. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  83. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
  84. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/settings.css +0 -0
  85. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/settings.html +0 -0
  86. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/settings_ui/settings.js +0 -0
  87. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/smoke_test.py +0 -0
  88. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  89. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/strict_bps.py +0 -0
  90. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/__init__.py +0 -0
  91. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/auto_backup.py +0 -0
  92. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/backup.py +0 -0
  93. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  94. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/component_api.py +0 -0
  95. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/component_config_reads.py +0 -0
  96. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/component_devices.py +0 -0
  97. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/component_registries.py +0 -0
  98. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  99. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  100. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/device_control.py +0 -0
  101. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/enhanced.py +0 -0
  102. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/helpers.py +0 -0
  103. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/radio/__init__.py +0 -0
  104. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/radio/base.py +0 -0
  105. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/radio/matter.py +0 -0
  106. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/radio/thread.py +0 -0
  107. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  108. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/radio/zwave.py +0 -0
  109. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/reference_validator.py +0 -0
  110. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/registry.py +0 -0
  111. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  112. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  113. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  114. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  115. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  116. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  117. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  118. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  119. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  120. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  121. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_addons.py +0 -0
  122. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_areas.py +0 -0
  123. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  124. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  125. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_calendar.py +0 -0
  126. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_camera.py +0 -0
  127. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_categories.py +0 -0
  128. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_code.py +0 -0
  129. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  130. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  131. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  132. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  133. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  134. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  135. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_dev.py +0 -0
  136. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_energy.py +0 -0
  137. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_entities.py +0 -0
  138. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  139. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_groups.py +0 -0
  140. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_hacs.py +0 -0
  141. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_history.py +0 -0
  142. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_integrations.py +0 -0
  143. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_labels.py +0 -0
  144. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_radio.py +0 -0
  145. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_registry.py +0 -0
  146. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_resources.py +0 -0
  147. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_search.py +0 -0
  148. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_service.py +0 -0
  149. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_services.py +0 -0
  150. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_system.py +0 -0
  151. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_themes.py +0 -0
  152. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_todo.py +0 -0
  153. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_traces.py +0 -0
  154. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_updates.py +0 -0
  155. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_utility.py +0 -0
  156. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  157. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  158. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  159. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/tools_zones.py +0 -0
  160. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/util_helpers.py +0 -0
  161. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/tools/validation_middleware.py +0 -0
  162. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/transforms/__init__.py +0 -0
  163. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/transforms/categorized_search.py +0 -0
  164. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  165. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/transport_security.py +0 -0
  166. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/update_check.py +0 -0
  167. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/__init__.py +0 -0
  168. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/config_hash.py +0 -0
  169. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/config_write_lock.py +0 -0
  170. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/data_paths.py +0 -0
  171. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/domain_handlers.py +0 -0
  172. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  173. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  174. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/operation_manager.py +0 -0
  175. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/python_sandbox.py +0 -0
  176. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/skill_loader.py +0 -0
  177. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/utils/usage_logger.py +0 -0
  178. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/visibility/__init__.py +0 -0
  179. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/visibility/enforcement.py +0 -0
  180. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/visibility/model.py +0 -0
  181. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/visibility/persistence.py +0 -0
  182. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp/visibility/resolver.py +0 -0
  183. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  184. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  185. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  186. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  187. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  188. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/tests/__init__.py +0 -0
  189. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/tests/test_constants.py +0 -0
  190. {ha_mcp_dev-7.14.2.dev2109 → ha_mcp_dev-7.14.2.dev2113}/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.14.2.dev2109
3
+ Version: 7.14.2.dev2113
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.14.2.dev2109"
7
+ version = "7.14.2.dev2113"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  # Upper bound widened to <3.15 so the package installs on Home Assistant's
@@ -31,7 +31,7 @@ import stat # noqa: E402
31
31
  import sys # noqa: E402
32
32
  import threading # noqa: E402
33
33
  from collections.abc import Coroutine # noqa: E402
34
- from typing import TYPE_CHECKING, Any # noqa: E402
34
+ from typing import TYPE_CHECKING, Any, NoReturn # noqa: E402
35
35
 
36
36
  from fastmcp.exceptions import ToolError # noqa: E402
37
37
  from pydantic import ValidationError as PydanticValidationError # noqa: E402
@@ -131,6 +131,16 @@ SHUTDOWN_TIMEOUT_SECONDS = 2.0
131
131
  _shutdown_event: asyncio.Event | None = None
132
132
  _shutdown_in_progress = False
133
133
 
134
+ # Set by the stdio entry point (main) so signal-initiated shutdown arms a
135
+ # hard-deadline watchdog. HTTP entry points leave this False.
136
+ _force_exit_on_shutdown = False
137
+
138
+ # Hard deadline for a signal-initiated stdio shutdown. Must exceed the sum
139
+ # of the graceful phases (server-stop wait + resource cleanup + task
140
+ # cancellation, SHUTDOWN_TIMEOUT_SECONDS each) so it only fires when the
141
+ # graceful path is truly stuck (issue #2027).
142
+ _SHUTDOWN_WATCHDOG_SECONDS = 10.0
143
+
134
144
  # Stdin error message for Docker without -i flag
135
145
  _STDIN_ERROR_MESSAGE = """
136
146
  ==============================================================================
@@ -589,16 +599,29 @@ async def _cleanup_resources() -> None:
589
599
 
590
600
 
591
601
  async def _cancel_tasks(*tasks: asyncio.Task) -> None:
592
- """Cancel tasks and wait for completion, swallowing CancelledError."""
593
- for task in tasks:
594
- if not task.done():
595
- task.cancel()
596
- try:
597
- await task
598
- except asyncio.CancelledError:
599
- # Expected: we just cancelled this task, swallow its
600
- # CancelledError so remaining tasks still get awaited.
601
- pass
602
+ """Cancel tasks and wait for completion, bounding the wait.
603
+
604
+ A task can ignore cancellation indefinitely — the stdio transport does
605
+ when a blocking stdin read is in flight (issue #2027) — so the wait is
606
+ bounded instead of awaiting each task unboundedly. Stragglers are
607
+ logged and abandoned; the stdio entry point force-exits anyway.
608
+ """
609
+ pending = [task for task in tasks if not task.done()]
610
+ if not pending:
611
+ return
612
+ for task in pending:
613
+ task.cancel()
614
+ done, still_pending = await asyncio.wait(pending, timeout=SHUTDOWN_TIMEOUT_SECONDS)
615
+ if still_pending:
616
+ logger.warning(
617
+ "%d task(s) ignored cancellation during shutdown", len(still_pending)
618
+ )
619
+ for task in done:
620
+ if task.cancelled():
621
+ continue
622
+ exc = task.exception()
623
+ if exc is not None:
624
+ logger.warning(f"Task raised during shutdown: {exc!r}")
602
625
 
603
626
 
604
627
  async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
@@ -622,14 +645,22 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
622
645
  if shutdown_task in done:
623
646
  logger.info("Shutdown signal received, stopping server...")
624
647
  server_task.cancel()
625
- try:
626
- await asyncio.wait_for(server_task, timeout=SHUTDOWN_TIMEOUT_SECONDS)
627
- except TimeoutError:
648
+ # asyncio.wait, not wait_for: wait_for's timeout path awaits the
649
+ # cancellation completing, which hangs when the stdio transport
650
+ # ignores cancellation mid-stdin-read (issue #2027). wait simply
651
+ # returns after the timeout, leaving the straggler abandoned.
652
+ stopped, _ = await asyncio.wait(
653
+ {server_task}, timeout=SHUTDOWN_TIMEOUT_SECONDS
654
+ )
655
+ if server_task in stopped:
656
+ try:
657
+ server_task.result()
658
+ except asyncio.CancelledError:
659
+ # Expected: we just cancelled server_task above; swallow
660
+ # its CancelledError so shutdown can proceed to cleanup.
661
+ pass
662
+ else:
628
663
  logger.warning("Server did not stop within timeout")
629
- except asyncio.CancelledError:
630
- # Expected: we just cancelled server_task above; swallow its
631
- # CancelledError so shutdown can proceed to cleanup.
632
- pass
633
664
  elif server_task in done:
634
665
  # Server task finished on its own (no shutdown signal). Re-raise any
635
666
  # exception it captured so a hard startup failure surfaces as a
@@ -696,6 +727,20 @@ def _signal_handler(signum: int, frame: Any) -> None:
696
727
  sys.exit(1)
697
728
 
698
729
  _shutdown_in_progress = True
730
+
731
+ if _force_exit_on_shutdown:
732
+ # The SDK's stdio teardown hangs if a stdin read is in flight when
733
+ # the server task is cancelled: the CancelledError gets stuck being
734
+ # thrown into stdio_server's task group while the reader thread is
735
+ # parked in a blocking stdin read, so asyncio.run never returns
736
+ # (issue #2027 — leftover stdio instances that never exit).
737
+ # Guarantee the process dies even if the graceful path is stuck.
738
+ # Armed BEFORE the log line below: logging writes to stderr, and a
739
+ # full stderr pipe would block this handler before it armed anything.
740
+ watchdog = threading.Timer(_SHUTDOWN_WATCHDOG_SECONDS, _shutdown_watchdog_fire)
741
+ watchdog.daemon = True
742
+ watchdog.start()
743
+
699
744
  logger.info(f"Received {sig_name}, initiating graceful shutdown...")
700
745
 
701
746
  # Signal the shutdown event if we have an event loop
@@ -720,6 +765,61 @@ async def _run_with_graceful_shutdown() -> None:
720
765
  await _run_with_shutdown(_get_mcp().run_async(show_banner=_get_show_banner()))
721
766
 
722
767
 
768
+ def _force_exit(code: int) -> NoReturn:
769
+ """Exit the stdio process without interpreter finalization (issue #2027).
770
+
771
+ The MCP SDK's stdio transport reads stdin through anyio's worker-thread
772
+ pool, so a daemon thread is parked in a blocking stdin read holding the
773
+ ``BufferedReader`` lock whenever no message is in flight. If the process
774
+ exits while that read is pending — SIGTERM, or the client vanishing
775
+ mid-read — interpreter finalization tries to close stdin, cannot acquire
776
+ the lock, and CPython aborts: ``Fatal Python error: _enter_buffered_busy``
777
+ → SIGABRT (a crash dialog on macOS). The reader lives in the SDK, so the
778
+ entry point neutralizes it instead: flush what matters, then skip
779
+ finalization entirely. By this point ``_run_with_shutdown`` has already
780
+ run resource cleanup, and a stdio server persists nothing at exit.
781
+
782
+ The flush is best-effort and time-bounded in a daemon thread: if the
783
+ client stopped draining stdout/stderr, the SDK's writer thread can be
784
+ blocked on the full pipe holding the buffered-writer lock, and flushing
785
+ inline would wait on that same lock forever — turning the hard-exit
786
+ path (including the shutdown watchdog) back into a hang. ``os._exit``
787
+ runs unconditionally once the bounded wait elapses.
788
+ """
789
+
790
+ def _best_effort_flush() -> None:
791
+ logging.shutdown()
792
+ for stream in (sys.stdout, sys.stderr):
793
+ try:
794
+ stream.flush()
795
+ except Exception: # nothing actionable at exit
796
+ pass
797
+
798
+ try:
799
+ flusher = threading.Thread(target=_best_effort_flush, daemon=True)
800
+ flusher.start()
801
+ flusher.join(timeout=1.0)
802
+ finally:
803
+ os._exit(code)
804
+
805
+
806
+ def _shutdown_watchdog_fire() -> None:
807
+ """Hard-deadline fallback for a stuck graceful shutdown (issue #2027).
808
+
809
+ The warning is fire-and-forget in a daemon thread: logging writes to
810
+ stderr, and a full stderr pipe blocking the warning would block the
811
+ watchdog itself — the very condition it exists to escape.
812
+ """
813
+ threading.Thread(
814
+ target=lambda: logger.warning(
815
+ "Graceful shutdown did not complete within %.0fs; forcing exit",
816
+ _SHUTDOWN_WATCHDOG_SECONDS,
817
+ ),
818
+ daemon=True,
819
+ ).start()
820
+ _force_exit(0)
821
+
822
+
723
823
  # CLI entry point (for pyproject.toml) - use FastMCP's built-in runner
724
824
  def main() -> None:
725
825
  """Run server via CLI using FastMCP's stdio transport."""
@@ -758,7 +858,28 @@ def main() -> None:
758
858
  # Best-effort: failure logs a warning but doesn't block MCP startup.
759
859
  _maybe_spawn_settings_sidecar()
760
860
 
761
- _run_entrypoint(_run_with_graceful_shutdown(), "Server")
861
+ # Route every exit through _force_exit: letting interpreter finalization
862
+ # run aborts when the SDK's stdin reader thread still holds the stdin
863
+ # lock. _run_entrypoint always leaves via sys.exit; the BaseException
864
+ # arm covers hard-stop paths (e.g. the re-raised CancelledError from the
865
+ # #1544 handling) that would otherwise bypass the force-exit. It also
866
+ # lets the signal handler arm the shutdown watchdog (stdio only).
867
+ global _force_exit_on_shutdown
868
+ _force_exit_on_shutdown = True
869
+ code = 1 # abnormal termination unless an exit says otherwise
870
+ try:
871
+ _run_entrypoint(_run_with_graceful_shutdown(), "Server")
872
+ code = 0
873
+ except SystemExit as exc:
874
+ if isinstance(exc.code, int):
875
+ code = exc.code
876
+ elif exc.code is None:
877
+ code = 0
878
+ # any other code (an error-message string) keeps the default 1
879
+ except BaseException:
880
+ logger.error("Server terminated abnormally", exc_info=True)
881
+ finally:
882
+ _force_exit(code)
762
883
 
763
884
 
764
885
  def _maybe_spawn_settings_sidecar() -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.14.2.dev2109
3
+ Version: 7.14.2.dev2113
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