ha-mcp-dev 8.4.3.dev2563__tar.gz → 8.4.3.dev2564__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 (310) hide show
  1. {ha_mcp_dev-8.4.3.dev2563/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.4.3.dev2564}/PKG-INFO +1 -1
  2. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/pyproject.toml +1 -1
  3. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/config.py +10 -0
  4. ha_mcp_dev-8.4.3.dev2564/src/ha_mcp/ha_request_queue.py +145 -0
  5. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/middleware.py +2 -37
  6. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/read_only.py +1 -1
  7. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/server.py +9 -0
  8. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_advanced.py +12 -11
  9. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/en.json +4 -0
  10. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/settings.js +2 -0
  11. ha_mcp_dev-8.4.3.dev2564/src/ha_mcp/tool_dispatch.py +39 -0
  12. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/enforcement.py +1 -1
  13. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  14. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/SOURCES.txt +2 -0
  15. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/LICENSE +0 -0
  16. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/MANIFEST.in +0 -0
  17. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/README.md +0 -0
  18. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/setup.cfg +0 -0
  19. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/__init__.py +0 -0
  20. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/__main__.py +0 -0
  21. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_pypi_marker +0 -0
  22. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_rich_compat.py +0 -0
  23. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/__init__.py +0 -0
  24. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/requirements.txt +0 -0
  25. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
  26. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
  27. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
  28. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
  29. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
  30. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
  31. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
  32. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
  33. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
  34. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
  35. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
  36. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
  37. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
  38. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/client.py +0 -0
  39. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
  40. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
  41. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
  42. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
  43. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
  44. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
  45. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
  46. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
  47. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
  48. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
  49. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
  50. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
  51. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
  52. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
  53. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
  54. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
  55. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
  56. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
  57. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
  58. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
  59. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
  60. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
  61. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/server.py +0 -0
  62. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
  63. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
  64. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
  65. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
  66. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
  67. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
  68. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
  69. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
  70. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
  71. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
  72. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
  73. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
  74. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
  75. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
  76. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
  77. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
  78. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
  79. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
  80. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_vendor/websockets/version.py +0 -0
  81. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/_version.py +0 -0
  82. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/__init__.py +0 -0
  83. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/consent_form.py +0 -0
  84. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/oidc_compat.py +0 -0
  85. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/auth/provider.py +0 -0
  86. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/backup_manager.py +0 -0
  87. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/browser_landing.py +0 -0
  88. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/__init__.py +0 -0
  89. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/rest_client.py +0 -0
  90. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/supervisor_client.py +0 -0
  91. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/websocket_client.py +0 -0
  92. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/client/websocket_listener.py +0 -0
  93. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  94. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  95. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  96. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  97. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  98. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  99. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/errors.py +0 -0
  100. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/hacs_auto_refresh.py +0 -0
  101. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/llm_exposure.py +0 -0
  102. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/log_filters.py +0 -0
  103. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/__init__.py +0 -0
  104. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/approval_queue.py +0 -0
  105. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/editing.py +0 -0
  106. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/evaluator.py +0 -0
  107. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/handlers.py +0 -0
  108. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/model.py +0 -0
  109. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/persistence.py +0 -0
  110. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/policy/value_sources.py +0 -0
  111. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/py.typed +0 -0
  112. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/redaction.py +0 -0
  113. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/renamed_tools.py +0 -0
  114. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  115. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  116. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  117. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  118. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  119. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  120. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  121. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  122. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  123. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  124. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/area-target-over-entity-list/case.yaml +0 -0
  125. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/arrive-home-automation/case.yaml +0 -0
  126. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/assist-shortcut-card/case.yaml +0 -0
  127. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/avg-temperature/case.yaml +0 -0
  128. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/backup-deletion-confirmation/case.yaml +0 -0
  129. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/blueprint-authoring/case.yaml +0 -0
  130. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/button-event-entity/case.yaml +0 -0
  131. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/device-id-trigger-migration/case.yaml +0 -0
  132. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/helper-flow-fields-not-platform-yaml/case.yaml +0 -0
  133. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/media-player-song/case.yaml +0 -0
  134. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/motion-light-automation-mode/case.yaml +0 -0
  135. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/rename-entity-impact-analysis/case.yaml +0 -0
  136. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/template-attributes-yaml-exception/case.yaml +0 -0
  137. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/timer-finished-automation/case.yaml +0 -0
  138. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/evals/vacuum-last-clean/case.yaml +0 -0
  139. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/scripts/check_eval_cases.py +0 -0
  140. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  141. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  142. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  143. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/backups.md +0 -0
  144. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  145. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  146. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  147. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  148. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  149. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  150. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  151. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  152. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  153. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  154. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  155. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/__init__.py +0 -0
  156. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  157. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  158. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  159. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  160. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  161. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  162. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_locale_policy.py +0 -0
  163. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  164. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  165. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_theme.py +0 -0
  166. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  167. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  168. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
  169. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  170. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
  171. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/es.json +0 -0
  172. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
  173. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/it.json +0 -0
  174. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/ko.json +0 -0
  175. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
  176. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
  177. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  178. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
  179. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/tlh.json +0 -0
  180. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
  181. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/settings.css +0 -0
  182. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/settings_ui/settings.html +0 -0
  183. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/smoke_test.py +0 -0
  184. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  185. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/strict_bps.py +0 -0
  186. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/__init__.py +0 -0
  187. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/auto_backup.py +0 -0
  188. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/backup.py +0 -0
  189. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  190. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/best_practice_result.py +0 -0
  191. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/best_practice_variables.py +0 -0
  192. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/blueprint_sources.py +0 -0
  193. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/blueprint_substitute.py +0 -0
  194. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/blueprint_write.py +0 -0
  195. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/bulk_selector.py +0 -0
  196. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_api.py +0 -0
  197. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_config_entries.py +0 -0
  198. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_config_reads.py +0 -0
  199. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_devices.py +0 -0
  200. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_registries.py +0 -0
  201. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  202. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  203. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
  204. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
  205. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
  206. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_identity.py +0 -0
  207. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
  208. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
  209. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/device_control.py +0 -0
  210. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/error_log_parsing.py +0 -0
  211. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/hacs_registration.py +0 -0
  212. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/helpers.py +0 -0
  213. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
  214. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_common.py +0 -0
  215. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_sources.py +0 -0
  216. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_sources_fault.py +0 -0
  217. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/log_sources_supervisor.py +0 -0
  218. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/__init__.py +0 -0
  219. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/base.py +0 -0
  220. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/matter.py +0 -0
  221. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/thread.py +0 -0
  222. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  223. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/radio/zwave.py +0 -0
  224. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/reference_validator.py +0 -0
  225. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/registry.py +0 -0
  226. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
  227. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  228. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  229. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  230. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  231. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  232. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  233. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_graph.py +0 -0
  234. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  235. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  236. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  237. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  238. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_addons.py +0 -0
  239. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_areas.py +0 -0
  240. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
  241. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  242. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  243. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_calendar.py +0 -0
  244. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_camera.py +0 -0
  245. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_categories.py +0 -0
  246. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_code.py +0 -0
  247. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  248. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  249. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  250. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  251. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  252. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  253. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_dev.py +0 -0
  254. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_energy.py +0 -0
  255. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_entities.py +0 -0
  256. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  257. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_groups.py +0 -0
  258. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_hacs.py +0 -0
  259. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_history.py +0 -0
  260. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_integrations.py +0 -0
  261. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_labels.py +0 -0
  262. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_logs.py +0 -0
  263. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_radio.py +0 -0
  264. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_registry.py +0 -0
  265. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_resources.py +0 -0
  266. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_search.py +0 -0
  267. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_security_policy.py +0 -0
  268. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_service.py +0 -0
  269. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_services.py +0 -0
  270. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_system.py +0 -0
  271. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_themes.py +0 -0
  272. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_todo.py +0 -0
  273. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_traces.py +0 -0
  274. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_updates.py +0 -0
  275. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_utility.py +0 -0
  276. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  277. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  278. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  279. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/tools_zones.py +0 -0
  280. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/util_helpers.py +0 -0
  281. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/tools/validation_middleware.py +0 -0
  282. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transforms/__init__.py +0 -0
  283. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transforms/categorized_search.py +0 -0
  284. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  285. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/transport_security.py +0 -0
  286. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/update_check.py +0 -0
  287. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/__init__.py +0 -0
  288. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/config_hash.py +0 -0
  289. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/config_write_lock.py +0 -0
  290. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/data_paths.py +0 -0
  291. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/device_registry_semantics.py +0 -0
  292. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/domain_handlers.py +0 -0
  293. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/entity_membership.py +0 -0
  294. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  295. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  296. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/operation_manager.py +0 -0
  297. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/python_sandbox.py +0 -0
  298. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/skill_loader.py +0 -0
  299. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/utils/usage_logger.py +0 -0
  300. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/__init__.py +0 -0
  301. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/model.py +0 -0
  302. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/persistence.py +0 -0
  303. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp/visibility/resolver.py +0 -0
  304. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  305. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  306. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  307. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  308. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/tests/__init__.py +0 -0
  309. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/tests/test_constants.py +0 -0
  310. {ha_mcp_dev-8.4.3.dev2563 → ha_mcp_dev-8.4.3.dev2564}/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: 8.4.3.dev2563
3
+ Version: 8.4.3.dev2564
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 = "8.4.3.dev2563"
7
+ version = "8.4.3.dev2564"
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
@@ -62,6 +62,10 @@ class Settings(BaseSettings):
62
62
  # Tool configuration
63
63
  fuzzy_threshold: int = Field(60, alias="FUZZY_THRESHOLD")
64
64
 
65
+ # Optional process-wide outer tool-call concurrency. Zero preserves the
66
+ # existing unlimited behavior; constrained installs can opt into queuing.
67
+ ha_tool_concurrency: int = Field(0, ge=0, le=32, alias="HA_TOOL_CONCURRENCY")
68
+
65
69
  # Smart-search config-fetch time budgets (seconds). Bound how long
66
70
  # ha_search spends fetching automation/script/scene
67
71
  # definitions during the per-id fallback before reporting a partial
@@ -890,6 +894,9 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
890
894
  True,
891
895
  ),
892
896
  # Operations.
897
+ AdvancedField(
898
+ "ha_tool_concurrency", "HA_TOOL_CONCURRENCY", int, "operations", True
899
+ ),
893
900
  AdvancedField("backup_hint", "BACKUP_HINT", str, "operations", True),
894
901
  AdvancedField("enable_websocket", "ENABLE_WEBSOCKET", bool, "operations", True),
895
902
  # Dashboard-screenshot engine URL (#1538): docker/.env users could set
@@ -1000,6 +1007,7 @@ _ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
1000
1007
  "scene_config_time_budget": (1.0, 600.0),
1001
1008
  "individual_config_timeout": (1.0, 600.0),
1002
1009
  "individual_fetch_batch_size": (1, 100),
1010
+ "ha_tool_concurrency": (1, 32),
1003
1011
  "code_mode_max_duration": (1.0, 300.0),
1004
1012
  "code_mode_max_memory": (1_048_576, 268_435_456),
1005
1013
  "code_mode_max_recursion": (1, 10_000),
@@ -1015,6 +1023,7 @@ _ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
1015
1023
  # emits min=sentinel so the number input can still express "off"; the
1016
1024
  # override-apply and UI-POST paths accept the sentinel OR the bounded range.
1017
1025
  _ADVANCED_SETTINGS_SENTINELS: dict[str, int] = {
1026
+ "ha_tool_concurrency": 0,
1018
1027
  "sidecar_pin_port": 0,
1019
1028
  }
1020
1029
 
@@ -1036,6 +1045,7 @@ _ADVANCED_SETTINGS_CHOICES: dict[str, tuple[str, ...]] = {
1036
1045
  # batches addon-origin writes and POSTs them via Supervisor.
1037
1046
  ADDON_SYNCED_ADVANCED_FIELDS: tuple[str, ...] = (
1038
1047
  "backup_hint",
1048
+ "ha_tool_concurrency",
1039
1049
  "verify_ssl",
1040
1050
  )
1041
1051
 
@@ -0,0 +1,145 @@
1
+ """Cross-session concurrency control for Home Assistant tool calls."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import logging
7
+ from contextvars import ContextVar
8
+ from typing import Any
9
+
10
+ from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
11
+
12
+ from .errors import create_timeout_error
13
+ from .tool_dispatch import (
14
+ CALL_PROXY_META_TOOLS,
15
+ LOCAL_ONLY_TOOLS,
16
+ OUTER_QUEUE_ORCHESTRATORS,
17
+ is_approval_management_call,
18
+ )
19
+ from .tools.helpers import raise_tool_error, safe_progress
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ _QUEUE_WAIT_TIMEOUT_SECONDS = 60.0
24
+ _QUEUE_WAIT_HEARTBEAT_SECONDS = 15.0
25
+ _QUEUE_WAIT_WARNING_SECONDS = 15.0
26
+
27
+
28
+ def _bypasses_outer_queue(name: str, args: dict[str, Any]) -> bool:
29
+ """Return whether an outer envelope must run without a queue slot.
30
+
31
+ Proxy and orchestration envelopes leave admission to their nested dispatches.
32
+ Local search does not contact Home Assistant. Approval-management actions
33
+ must remain available while all configured slots are occupied.
34
+ """
35
+ return (
36
+ name in CALL_PROXY_META_TOOLS
37
+ or name in LOCAL_ONLY_TOOLS
38
+ or name in OUTER_QUEUE_ORCHESTRATORS
39
+ or is_approval_management_call(name, args)
40
+ )
41
+
42
+
43
+ class HomeAssistantRequestQueueMiddleware(Middleware):
44
+ """Bound concurrent outer tool calls while allowing nested redispatch."""
45
+
46
+ def __init__(self, max_concurrency: int = 1) -> None:
47
+ if not 1 <= max_concurrency <= 32:
48
+ raise ValueError(
49
+ "ha_tool_concurrency middleware requires a value between 1 and 32; "
50
+ "0 is handled by not registering the middleware"
51
+ )
52
+ self._semaphore = asyncio.Semaphore(max_concurrency)
53
+ self._depth: ContextVar[int] = ContextVar(
54
+ f"ha_request_queue_depth_{id(self)}", default=0
55
+ )
56
+
57
+ async def _acquire_slot(self, context: MiddlewareContext, name: str) -> None:
58
+ """Acquire an outer-call slot with progress and a bounded wait."""
59
+ loop = asyncio.get_running_loop()
60
+ started = loop.time()
61
+ warned = False
62
+
63
+ if not self._semaphore.locked():
64
+ await self._semaphore.acquire()
65
+ logger.debug("HA tool queue: acquired slot tool=%s wait=0.000s", name)
66
+ return
67
+
68
+ logger.debug("HA tool queue: waiting for slot tool=%s", name)
69
+
70
+ while True:
71
+ elapsed = loop.time() - started
72
+ remaining = _QUEUE_WAIT_TIMEOUT_SECONDS - elapsed
73
+ if remaining <= 0:
74
+ logger.warning(
75
+ "HA tool queue: timed out waiting for slot tool=%s wait=%.1fs",
76
+ name,
77
+ elapsed,
78
+ )
79
+ raise_tool_error(
80
+ create_timeout_error(
81
+ "Home Assistant tool queue admission",
82
+ _QUEUE_WAIT_TIMEOUT_SECONDS,
83
+ details=(
84
+ f"Tool '{name}' did not start because all configured "
85
+ "Home Assistant tool slots remained busy."
86
+ ),
87
+ context={"tool": name, "phase": "queue_wait"},
88
+ )
89
+ )
90
+
91
+ ctx = getattr(context, "fastmcp_context", None)
92
+ await safe_progress(
93
+ ctx,
94
+ progress=0,
95
+ total=0,
96
+ message=f"Waiting for Home Assistant tool capacity: {name}",
97
+ )
98
+ try:
99
+ await asyncio.wait_for(
100
+ self._semaphore.acquire(),
101
+ timeout=min(_QUEUE_WAIT_HEARTBEAT_SECONDS, remaining),
102
+ )
103
+ except TimeoutError:
104
+ elapsed = loop.time() - started
105
+ if not warned and elapsed >= _QUEUE_WAIT_WARNING_SECONDS:
106
+ warned = True
107
+ logger.warning(
108
+ "HA tool queue: still waiting for slot tool=%s wait=%.1fs",
109
+ name,
110
+ elapsed,
111
+ )
112
+ continue
113
+
114
+ logger.debug(
115
+ "HA tool queue: acquired slot tool=%s wait=%.3fs",
116
+ name,
117
+ loop.time() - started,
118
+ )
119
+ return
120
+
121
+ async def on_call_tool(
122
+ self, context: MiddlewareContext, call_next: CallNext
123
+ ) -> Any:
124
+ if _bypasses_outer_queue(context.message.name, context.message.arguments or {}):
125
+ return await call_next(context)
126
+
127
+ depth = self._depth.get()
128
+ if depth:
129
+ token = self._depth.set(depth + 1)
130
+ try:
131
+ return await call_next(context)
132
+ finally:
133
+ self._depth.reset(token)
134
+
135
+ name = context.message.name
136
+ await self._acquire_slot(context, name)
137
+ try:
138
+ token = self._depth.set(1)
139
+ try:
140
+ return await call_next(context)
141
+ finally:
142
+ self._depth.reset(token)
143
+ finally:
144
+ self._semaphore.release()
145
+ logger.debug("HA tool queue: released slot tool=%s", name)
@@ -13,6 +13,7 @@ from fastmcp.server.middleware.middleware import CallNext, Middleware, Middlewar
13
13
 
14
14
  from ..errors import ErrorCode, create_error_response
15
15
  from ..renamed_tools import current_tool_name
16
+ from ..tool_dispatch import CALL_PROXY_META_TOOLS, is_approval_management_call
16
17
  from ..tools.helpers import raise_tool_error, safe_progress
17
18
  from .approval_queue import ApprovalQueue, PendingApproval, compute_args_hash
18
19
  from .evaluator import (
@@ -26,48 +27,12 @@ from .model import Policy, Rule
26
27
 
27
28
  logger = logging.getLogger(__name__)
28
29
 
29
- # Dispatching call proxies are a strict subset of the ungated tool-search
30
- # meta-tools. Only these three execute their envelope's ``name``; search merely
31
- # returns catalog metadata and must not be unwrapped by other middleware.
32
- # Gating a call proxy directly would be wrong: rule predicates target the real
33
- # tool's args (for example args.domain), while the proxy receives a wrapped
34
- # {"name": "...", "arguments": {...}} envelope. Its dispatch re-enters the
35
- # middleware chain with the real name and args, so the inner call is gated there.
36
- CALL_PROXY_META_TOOLS = frozenset(
37
- {
38
- "ha_call_read_tool",
39
- "ha_call_write_tool",
40
- "ha_call_delete_tool",
41
- }
42
- )
43
- # Policy itself also leaves catalog search ungated; unlike the call proxies it
44
- # never dispatches a tool named in client-supplied arguments.
45
30
  PROXY_META_TOOLS = CALL_PROXY_META_TOOLS | {"ha_search_tools"}
46
31
 
47
- # ha_dev_manage_server actions that MANAGE the approval queue itself.
48
- # Gating these deadlocks by construction: with a wildcard (or
49
- # ha_dev_manage_server) rule in place, an MCP-only "approve" call would
50
- # itself require approval — creating a second pending entry instead of
51
- # deciding the first, so nothing can ever be approved through the tool.
52
- # Only the queue-management actions are exempt; update_source / restart
53
- # remain gateable like any other high-stakes action. The exemption is not
54
- # a free self-approval: approve/deny separately require the
55
- # dev_tools_security_policy_access setting (off by default, issue #2141).
56
- _APPROVAL_MANAGEMENT_TOOL = "ha_dev_manage_server"
57
- _APPROVAL_MANAGEMENT_ACTIONS = frozenset({"list_pending", "approve", "deny"})
58
-
59
-
60
- def _is_approval_management(name: str, args: dict[str, Any]) -> bool:
61
- """True for dev-tool calls that manage the approval queue itself."""
62
- return (
63
- name == _APPROVAL_MANAGEMENT_TOOL
64
- and args.get("action") in _APPROVAL_MANAGEMENT_ACTIONS
65
- )
66
-
67
32
 
68
33
  def _passes_ungated(name: str, args: dict[str, Any]) -> bool:
69
34
  """Calls that must bypass gating: proxy meta-tools + queue management."""
70
- return name in PROXY_META_TOOLS or _is_approval_management(name, args)
35
+ return name in PROXY_META_TOOLS or is_approval_management_call(name, args)
71
36
 
72
37
 
73
38
  class PolicyMiddleware(Middleware):
@@ -39,8 +39,8 @@ from fastmcp.tools import Tool
39
39
 
40
40
  from .config import get_global_settings
41
41
  from .errors import ErrorCode, create_error_response
42
- from .policy.middleware import CALL_PROXY_META_TOOLS
43
42
  from .renamed_tools import current_tool_name
43
+ from .tool_dispatch import CALL_PROXY_META_TOOLS
44
44
  from .tools.helpers import raise_tool_error
45
45
 
46
46
  if TYPE_CHECKING:
@@ -289,6 +289,15 @@ class HomeAssistantSmartMCPServer:
289
289
  # wraps the final tool surface (including the search proxies).
290
290
  self._apply_tool_security_policies()
291
291
 
292
+ # Constrained installs can opt into cross-session tool queuing. This
293
+ # sits after approval gates so a call awaiting approval holds no slot.
294
+ if self.settings.ha_tool_concurrency:
295
+ from .ha_request_queue import HomeAssistantRequestQueueMiddleware
296
+
297
+ self.mcp.add_middleware(
298
+ HomeAssistantRequestQueueMiddleware(self.settings.ha_tool_concurrency)
299
+ )
300
+
292
301
  # Known-secret-value scrub (#2157) — registered just before the
293
302
  # visibility outbound half so that half stays innermost (its scan
294
303
  # must see truly raw output; a secret value could embed a hidden
@@ -3,7 +3,8 @@
3
3
  Factory returning the ``get_advanced_settings`` / ``save_advanced_settings``
4
4
  handlers over the ``ADVANCED_SETTINGS_FIELDS`` registry. Most advanced
5
5
  fields write to the shared ``feature_flags.json`` override file;
6
- ``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``, ``verify_ssl``) route
6
+ ``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``, ``ha_tool_concurrency``,
7
+ ``verify_ssl``) route
7
8
  through the Supervisor add-on options in add-on mode instead.
8
9
 
9
10
  Handlers are module-level (own C901 budget); ``build_advanced_handlers``
@@ -131,10 +132,10 @@ async def _get_advanced_settings(
131
132
  Mirrors ``_get_feature_flags`` / ``_get_backup_config`` but for the
132
133
  ``ADVANCED_SETTINGS_FIELDS`` registry. Most advanced fields write to
133
134
  ``feature_flags.json`` via the shared override file in either deployment
134
- mode. ``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``, ``verify_ssl``)
135
- are an exception: in addon mode they have ``origin="addon"`` (editable)
136
- and saves route through Supervisor so the add-on Configuration tab and
137
- the web UI share state.
135
+ mode. ``ADDON_SYNCED_ADVANCED_FIELDS`` (``backup_hint``,
136
+ ``ha_tool_concurrency``, ``verify_ssl``) are an exception: in addon mode
137
+ they have ``origin="addon"`` (editable) and saves route through Supervisor
138
+ so the add-on Configuration tab and the web UI share state.
138
139
  """
139
140
  from ha_mcp.settings_ui import is_http_settings_mounted
140
141
 
@@ -327,7 +328,7 @@ def _validate_one_advanced_field(
327
328
  status_code=409,
328
329
  ),
329
330
  )
330
- # Addon-synced fields (e.g. backup_hint, verify_ssl) are editable in
331
+ # Addon-synced fields (backup_hint, ha_tool_concurrency, verify_ssl) are editable in
331
332
  # addon mode even though their env vars are set — start.py rewrites them
332
333
  # from /data/options.json on every boot, so route the write through
333
334
  # Supervisor instead of the override file.
@@ -525,11 +526,11 @@ async def _save_advanced_settings(
525
526
  ) -> JSONResponse:
526
527
  """Persist UI-edited advanced settings.
527
528
 
528
- Addon-synced fields (``backup_hint``, ``verify_ssl``) in add-on mode
529
- route through Supervisor; everything else atomically merges into the
530
- shared ``feature_flags.json`` override file. Either sink responds with
531
- ``restart_required=True`` (most advanced fields gate one-time startup
532
- paths).
529
+ Addon-synced fields (``backup_hint``, ``ha_tool_concurrency``,
530
+ ``verify_ssl``) in add-on mode route through Supervisor; everything else
531
+ atomically merges into the shared ``feature_flags.json`` override file.
532
+ Either sink responds with ``restart_required=True`` (most advanced fields
533
+ gate one-time startup paths).
533
534
  """
534
535
  from ..config import ADVANCED_SETTINGS_FIELDS
535
536
 
@@ -404,6 +404,8 @@
404
404
  "advanced.individual_config_timeout.help": "Timeout for each automation/script/scene config fetch. Range 1–600. Restart required.",
405
405
  "advanced.individual_fetch_batch_size.label": "Config fetch batch size",
406
406
  "advanced.individual_fetch_batch_size.help": "Concurrent per-ID config fetches. Lower toward 1 on serial HA servers. Range 1–100. Restart required.",
407
+ "advanced.ha_tool_concurrency.label": "Home Assistant tool concurrency",
408
+ "advanced.ha_tool_concurrency.help": "Optional limit on outer Home Assistant tool calls across all sessions. Internal REST, WebSocket, and per-tool fan-out concurrency is unchanged. 0 keeps unlimited behavior. A call waiting for capacity fails after 60 seconds. Range 0–32. Restart required.",
407
409
  "advanced.backup_hint.label": "Backup-hint level",
408
410
  "advanced.backup_hint.help": "Controls how strongly the LLM is prompted to take a full HA snapshot before risky writes.",
409
411
  "advanced.backup_hint.choices.strong": "Strong",
@@ -468,6 +470,8 @@
468
470
  "addon.pinned_tools.description": "Tools listed here are locked pinned — they appear at the top of the Tools tab and cannot be unpinned from the web Settings UI without unsetting this option first. Useful in combination with Tool Search. Comma-separated tool names.",
469
471
  "addon.verify_ssl.name": "Verify TLS certificate",
470
472
  "addon.verify_ssl.description": "Verify the Home Assistant server's TLS certificate. The app (add-on) connects via the Supervisor proxy, so this normally has no effect and should stay enabled. Disable only if you've reconfigured the app (add-on) to talk to HA over a public HTTPS hostname with a self-signed certificate or hostname mismatch. Disabling weakens transport security — leave on unless you know you need it. Requires restart to take effect.",
473
+ "addon.ha_tool_concurrency.name": "Home Assistant tool concurrency",
474
+ "addon.ha_tool_concurrency.description": "Optional limit on outer Home Assistant tool calls across all sessions. Internal REST, WebSocket, and per-tool fan-out concurrency is unchanged. 0 keeps unlimited behavior. A call waiting for capacity fails after 60 seconds.",
471
475
  "addon_stable.enable_tool_search.description": "Replace the full tool catalog with search-based discovery. Reduces idle context by roughly 90%, to about 5K tokens. ⚠️ Do NOT enable this in clients with their own built-in tool search / deferred tools (claude.ai, Claude Desktop, Claude Code) — the two search layers conflict, and the client's built-in tool search is the better choice there; leave this off. Whether tools are deferred depends on your client and model combination: use this when your setup loads the full catalog up front — models without native deferred tools (e.g. Gemini, OpenAI-compatible local models, Claude Haiku), clients that inline all tool schemas regardless of model (e.g. GitHub Copilot CLI), or smaller context windows. Some Codex models and ChatGPT include deferred tools too — check your client/model directly to confirm its features so you don't leave this enabled unnecessarily. Tools are found via ha_search_tools and executed via categorized proxies (read/write/delete). Requires an app (add-on) restart to take effect — then reconnect or refresh the MCP server in your AI client so it reloads the tool list. Restarting the app (add-on) alone does NOT refresh a client's cached tool list; this applies after changing any setting here."
472
476
  },
473
477
  "tool_groups": {},
@@ -3868,6 +3868,7 @@ const ADVANCED_FIELD_META = {
3868
3868
  timeout: { label: "HA request timeout (s)", help: "Per-request HTTP timeout. Range 1–600. Restart required." },
3869
3869
  max_retries: { label: "HA request max retries", help: "Retry budget per failed REST call. Range 0–20. Restart required." },
3870
3870
  verify_ssl: { label: "Verify SSL certificates", help: "Skip TLS verification only on trusted networks (self-signed certs, hostname mismatch). Restart required." },
3871
+ ha_tool_concurrency: { label: "Home Assistant tool concurrency", help: "Optional limit on outer Home Assistant tool calls across all MCP sessions. Internal REST, WebSocket, and per-tool fan-out concurrency is unchanged. 0 keeps unlimited behavior. A call waiting for capacity fails after 60 seconds. Range 0–32. Restart required." },
3871
3872
  fuzzy_threshold: { label: "Fuzzy-search threshold", help: "Lower = looser entity match. Range 0–100." },
3872
3873
  automation_config_time_budget: { label: "Automation config time budget (s)", help: "Max seconds deep search spends fetching automation configs before returning a partial result. Raise on instances with many automations. Range 1–600. Restart required." },
3873
3874
  script_config_time_budget: { label: "Script config time budget (s)", help: "Max seconds deep search spends fetching script configs before returning a partial result. Range 1–600. Restart required." },
@@ -3902,6 +3903,7 @@ const ADVANCED_FIELD_META = {
3902
3903
  // is per-request.
3903
3904
  const ADVANCED_RESTART_REQUIRED = new Set([
3904
3905
  "timeout", "max_retries", "verify_ssl",
3906
+ "ha_tool_concurrency",
3905
3907
  "enabled_tool_modules", "enable_websocket",
3906
3908
  "log_level", "debug",
3907
3909
  "mcp_server_name", "mcp_server_version", "environment",
@@ -0,0 +1,39 @@
1
+ """Shared classifications for tool dispatch middleware."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ # Call proxies dispatch the envelope's ``name`` back through middleware. Policy
8
+ # rules and read-only checks must apply to the real tool name and arguments, not
9
+ # the proxy envelope, so these outer calls bypass those checks.
10
+ CALL_PROXY_META_TOOLS = frozenset(
11
+ {
12
+ "ha_call_read_tool",
13
+ "ha_call_write_tool",
14
+ "ha_call_delete_tool",
15
+ }
16
+ )
17
+
18
+ LOCAL_ONLY_TOOLS = frozenset({"ha_search_tools"})
19
+
20
+ # Code mode is local orchestration that can issue many nested HA calls. Its
21
+ # outer envelope must not hold one slot while nested calls reuse that admission;
22
+ # each nested HA dispatch instead acquires an outer-call slot normally.
23
+ OUTER_QUEUE_ORCHESTRATORS = frozenset({"ha_manage_custom_tool"})
24
+
25
+ _APPROVAL_MANAGEMENT_TOOL = "ha_dev_manage_server"
26
+ _APPROVAL_MANAGEMENT_ACTIONS = frozenset({"list_pending", "approve", "deny"})
27
+
28
+
29
+ def is_approval_management_call(name: str, args: dict[str, Any]) -> bool:
30
+ """Return whether a dispatch manages the policy approval queue.
31
+
32
+ Gating or queueing these actions can deadlock approval by making the action
33
+ that decides a pending request wait behind that same blocked workload.
34
+ Update and restart actions remain subject to normal middleware behavior.
35
+ """
36
+ return (
37
+ name == _APPROVAL_MANAGEMENT_TOOL
38
+ and args.get("action") in _APPROVAL_MANAGEMENT_ACTIONS
39
+ )
@@ -62,8 +62,8 @@ from fastmcp.exceptions import ToolError
62
62
  from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
63
63
 
64
64
  from ..errors import ErrorCode, create_entity_not_found_error, create_error_response
65
- from ..policy.middleware import CALL_PROXY_META_TOOLS
66
65
  from ..renamed_tools import current_tool_name
66
+ from ..tool_dispatch import CALL_PROXY_META_TOOLS
67
67
  from ..tools.helpers import raise_tool_error
68
68
  from . import resolver
69
69
  from .model import VisibilityConfig
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 8.4.3.dev2563
3
+ Version: 8.4.3.dev2564
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
@@ -11,6 +11,7 @@ src/ha_mcp/backup_manager.py
11
11
  src/ha_mcp/browser_landing.py
12
12
  src/ha_mcp/config.py
13
13
  src/ha_mcp/errors.py
14
+ src/ha_mcp/ha_request_queue.py
14
15
  src/ha_mcp/hacs_auto_refresh.py
15
16
  src/ha_mcp/llm_exposure.py
16
17
  src/ha_mcp/log_filters.py
@@ -22,6 +23,7 @@ src/ha_mcp/server.py
22
23
  src/ha_mcp/smoke_test.py
23
24
  src/ha_mcp/stdio_settings_sidecar.py
24
25
  src/ha_mcp/strict_bps.py
26
+ src/ha_mcp/tool_dispatch.py
25
27
  src/ha_mcp/transport_security.py
26
28
  src/ha_mcp/update_check.py
27
29
  src/ha_mcp/_vendor/__init__.py