ha-mcp-dev 8.4.3.dev2548__tar.gz → 8.4.3.dev2552__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 (308) hide show
  1. {ha_mcp_dev-8.4.3.dev2548/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.4.3.dev2552}/PKG-INFO +4 -2
  2. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/README.md +3 -1
  3. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/pyproject.toml +1 -1
  4. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/transforms/categorized_search.py +116 -30
  5. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552/src/ha_mcp_dev.egg-info}/PKG-INFO +4 -2
  6. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/LICENSE +0 -0
  7. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/MANIFEST.in +0 -0
  8. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/setup.cfg +0 -0
  9. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_rich_compat.py +0 -0
  13. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/__init__.py +0 -0
  14. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/requirements.txt +0 -0
  15. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
  16. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
  17. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
  18. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
  19. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
  20. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
  21. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
  22. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
  23. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
  24. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
  25. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
  26. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
  27. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
  28. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/client.py +0 -0
  29. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
  30. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
  31. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
  32. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
  33. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
  34. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
  35. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
  36. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
  37. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
  38. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
  39. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
  40. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
  41. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
  42. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
  43. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
  44. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
  45. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
  46. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
  47. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
  48. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
  49. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
  50. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
  51. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/server.py +0 -0
  52. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
  53. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
  54. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
  55. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
  56. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
  57. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
  58. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
  59. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
  60. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
  61. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
  62. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
  63. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
  64. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
  65. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
  66. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
  67. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
  68. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
  69. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
  70. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_vendor/websockets/version.py +0 -0
  71. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/_version.py +0 -0
  72. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/auth/__init__.py +0 -0
  73. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/auth/consent_form.py +0 -0
  74. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/auth/oidc_compat.py +0 -0
  75. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/auth/provider.py +0 -0
  76. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/backup_manager.py +0 -0
  77. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/browser_landing.py +0 -0
  78. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/client/__init__.py +0 -0
  79. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/client/rest_client.py +0 -0
  80. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/client/supervisor_client.py +0 -0
  81. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/client/websocket_client.py +0 -0
  82. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/client/websocket_listener.py +0 -0
  83. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/config.py +0 -0
  84. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  85. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  86. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  87. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  88. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  89. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  90. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/errors.py +0 -0
  91. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/hacs_auto_refresh.py +0 -0
  92. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/llm_exposure.py +0 -0
  93. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/log_filters.py +0 -0
  94. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/__init__.py +0 -0
  95. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/approval_queue.py +0 -0
  96. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/editing.py +0 -0
  97. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/evaluator.py +0 -0
  98. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/handlers.py +0 -0
  99. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/middleware.py +0 -0
  100. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/model.py +0 -0
  101. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/persistence.py +0 -0
  102. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/policy/value_sources.py +0 -0
  103. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/py.typed +0 -0
  104. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/read_only.py +0 -0
  105. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/redaction.py +0 -0
  106. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/renamed_tools.py +0 -0
  107. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  108. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  109. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  110. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  111. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  112. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  113. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  114. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  115. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  116. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  117. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/area-target-over-entity-list/case.yaml +0 -0
  118. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/arrive-home-automation/case.yaml +0 -0
  119. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/assist-shortcut-card/case.yaml +0 -0
  120. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/avg-temperature/case.yaml +0 -0
  121. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/backup-deletion-confirmation/case.yaml +0 -0
  122. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/blueprint-authoring/case.yaml +0 -0
  123. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/button-event-entity/case.yaml +0 -0
  124. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/device-id-trigger-migration/case.yaml +0 -0
  125. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/helper-flow-fields-not-platform-yaml/case.yaml +0 -0
  126. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/media-player-song/case.yaml +0 -0
  127. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/motion-light-automation-mode/case.yaml +0 -0
  128. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/rename-entity-impact-analysis/case.yaml +0 -0
  129. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/template-attributes-yaml-exception/case.yaml +0 -0
  130. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/timer-finished-automation/case.yaml +0 -0
  131. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/evals/vacuum-last-clean/case.yaml +0 -0
  132. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/scripts/check_eval_cases.py +0 -0
  133. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  134. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  135. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  136. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/backups.md +0 -0
  137. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  138. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  139. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  140. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  141. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  142. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  143. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  144. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  145. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  146. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  147. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  148. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/server.py +0 -0
  149. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/__init__.py +0 -0
  150. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  151. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  152. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  153. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  154. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  155. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  156. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  157. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_locale_policy.py +0 -0
  158. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  159. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  160. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_theme.py +0 -0
  161. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  162. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  163. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
  164. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  165. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/en.json +0 -0
  166. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
  167. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/es.json +0 -0
  168. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
  169. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/it.json +0 -0
  170. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/ko.json +0 -0
  171. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
  172. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
  173. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  174. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
  175. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/tlh.json +0 -0
  176. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
  177. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/settings.css +0 -0
  178. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/settings.html +0 -0
  179. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/settings_ui/settings.js +0 -0
  180. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/smoke_test.py +0 -0
  181. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  182. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/strict_bps.py +0 -0
  183. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/__init__.py +0 -0
  184. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/auto_backup.py +0 -0
  185. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/backup.py +0 -0
  186. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  187. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/best_practice_result.py +0 -0
  188. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/best_practice_variables.py +0 -0
  189. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/blueprint_sources.py +0 -0
  190. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/blueprint_substitute.py +0 -0
  191. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/blueprint_write.py +0 -0
  192. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/bulk_selector.py +0 -0
  193. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/component_api.py +0 -0
  194. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/component_config_entries.py +0 -0
  195. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/component_config_reads.py +0 -0
  196. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/component_devices.py +0 -0
  197. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/component_registries.py +0 -0
  198. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  199. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  200. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
  201. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
  202. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
  203. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_identity.py +0 -0
  204. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
  205. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
  206. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/device_control.py +0 -0
  207. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/error_log_parsing.py +0 -0
  208. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/hacs_registration.py +0 -0
  209. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/helpers.py +0 -0
  210. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
  211. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/log_common.py +0 -0
  212. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/log_sources.py +0 -0
  213. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/log_sources_fault.py +0 -0
  214. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/log_sources_supervisor.py +0 -0
  215. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/radio/__init__.py +0 -0
  216. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/radio/base.py +0 -0
  217. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/radio/matter.py +0 -0
  218. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/radio/thread.py +0 -0
  219. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  220. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/radio/zwave.py +0 -0
  221. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/reference_validator.py +0 -0
  222. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/registry.py +0 -0
  223. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
  224. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  225. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  226. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  227. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  228. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  229. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  230. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_graph.py +0 -0
  231. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  232. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  233. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  234. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  235. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_addons.py +0 -0
  236. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_areas.py +0 -0
  237. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
  238. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  239. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  240. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_calendar.py +0 -0
  241. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_camera.py +0 -0
  242. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_categories.py +0 -0
  243. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_code.py +0 -0
  244. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  245. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  246. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  247. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  248. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  249. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  250. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_dev.py +0 -0
  251. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_energy.py +0 -0
  252. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_entities.py +0 -0
  253. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  254. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_groups.py +0 -0
  255. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_hacs.py +0 -0
  256. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_history.py +0 -0
  257. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_integrations.py +0 -0
  258. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_labels.py +0 -0
  259. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_logs.py +0 -0
  260. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_radio.py +0 -0
  261. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_registry.py +0 -0
  262. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_resources.py +0 -0
  263. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_search.py +0 -0
  264. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_security_policy.py +0 -0
  265. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_service.py +0 -0
  266. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_services.py +0 -0
  267. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_system.py +0 -0
  268. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_themes.py +0 -0
  269. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_todo.py +0 -0
  270. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_traces.py +0 -0
  271. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_updates.py +0 -0
  272. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_utility.py +0 -0
  273. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  274. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  275. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  276. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/tools_zones.py +0 -0
  277. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/util_helpers.py +0 -0
  278. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/tools/validation_middleware.py +0 -0
  279. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/transforms/__init__.py +0 -0
  280. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  281. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/transport_security.py +0 -0
  282. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/update_check.py +0 -0
  283. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/__init__.py +0 -0
  284. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/config_hash.py +0 -0
  285. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/config_write_lock.py +0 -0
  286. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/data_paths.py +0 -0
  287. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/device_registry_semantics.py +0 -0
  288. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/domain_handlers.py +0 -0
  289. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/entity_membership.py +0 -0
  290. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  291. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  292. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/operation_manager.py +0 -0
  293. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/python_sandbox.py +0 -0
  294. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/skill_loader.py +0 -0
  295. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/utils/usage_logger.py +0 -0
  296. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/visibility/__init__.py +0 -0
  297. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/visibility/enforcement.py +0 -0
  298. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/visibility/model.py +0 -0
  299. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/visibility/persistence.py +0 -0
  300. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp/visibility/resolver.py +0 -0
  301. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  302. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  303. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  304. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  305. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  306. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/tests/__init__.py +0 -0
  307. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/tests/test_constants.py +0 -0
  308. {ha_mcp_dev-8.4.3.dev2548 → ha_mcp_dev-8.4.3.dev2552}/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.dev2548
3
+ Version: 8.4.3.dev2552
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
@@ -380,6 +380,8 @@ Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA app). The full catal
380
380
 
381
381
  The proxy split lets MCP clients apply different permission policies per category (e.g. auto-approve reads, prompt for writes, confirm deletes) without parsing tool docstrings.
382
382
 
383
+ A `ha_manage_*` tool combines several operations, so it is reachable from more than one proxy: `ha_call_write_tool` and `ha_call_delete_tool` run the whole tool, while `ha_call_read_tool` runs only the read actions Read Only Mode approves (the same per-call verdict), so a manage tool with no approved read actions is not reachable there. Search results list each proxy the tool can be reached through. In Read Only Mode only `ha_call_read_tool` is listed; the other two still answer a client that holds them in a cached tool list, and any write they carry gets the Read Only Mode error rather than `Unknown tool`.
384
+
383
385
  | Setting | Default | Description |
384
386
  |---------|---------|-------------|
385
387
  | `ENABLE_TOOL_SEARCH` | `false` | Replace full tool catalog with search-based discovery (tools deferred behind on-demand search). |
@@ -394,7 +396,7 @@ The proxy split lets MCP clients apply different permission policies per categor
394
396
 
395
397
  Leave it off in clients with deferred tool loading (claude.ai, Claude Desktop, Claude Code); the full catalog has no idle cost there, direct calls skip the search step, and the client's built-in tool search is the better choice — there is no benefit to running ha-mcp's on top of it. Whether tools are deferred depends on the client and model combination: the same model can behave differently per client — GitHub Copilot CLI running Claude Sonnet/Opus inlines the full catalog and still benefits from tool search here. 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.
396
398
 
397
- > 🔄 **Refresh your client's tool list after changing this (or any) setting.** Toggling `ENABLE_TOOL_SEARCH` (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its **cached** tool list until it re-fetches. Restarting the app or Home Assistant does **not** refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but return `Unknown tool` when called. ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a **different name**.
399
+ > 🔄 **Refresh your client's tool list after changing this (or any) setting.** Toggling `ENABLE_TOOL_SEARCH` (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its **cached** tool list until it re-fetches. Restarting the app or Home Assistant does **not** refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but fail when called (`Unknown tool`, or the Read Only Mode error for a write tool that mode hides). ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a **different name**.
398
400
 
399
401
  For the HA app, the same option is documented in [`homeassistant-addon/DOCS.md`](homeassistant-addon/DOCS.md#enable_tool_search) along with the in-app settings UI for fine-grained tool enable/disable/pin.
400
402
 
@@ -348,6 +348,8 @@ Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA app). The full catal
348
348
 
349
349
  The proxy split lets MCP clients apply different permission policies per category (e.g. auto-approve reads, prompt for writes, confirm deletes) without parsing tool docstrings.
350
350
 
351
+ A `ha_manage_*` tool combines several operations, so it is reachable from more than one proxy: `ha_call_write_tool` and `ha_call_delete_tool` run the whole tool, while `ha_call_read_tool` runs only the read actions Read Only Mode approves (the same per-call verdict), so a manage tool with no approved read actions is not reachable there. Search results list each proxy the tool can be reached through. In Read Only Mode only `ha_call_read_tool` is listed; the other two still answer a client that holds them in a cached tool list, and any write they carry gets the Read Only Mode error rather than `Unknown tool`.
352
+
351
353
  | Setting | Default | Description |
352
354
  |---------|---------|-------------|
353
355
  | `ENABLE_TOOL_SEARCH` | `false` | Replace full tool catalog with search-based discovery (tools deferred behind on-demand search). |
@@ -362,7 +364,7 @@ The proxy split lets MCP clients apply different permission policies per categor
362
364
 
363
365
  Leave it off in clients with deferred tool loading (claude.ai, Claude Desktop, Claude Code); the full catalog has no idle cost there, direct calls skip the search step, and the client's built-in tool search is the better choice — there is no benefit to running ha-mcp's on top of it. Whether tools are deferred depends on the client and model combination: the same model can behave differently per client — GitHub Copilot CLI running Claude Sonnet/Opus inlines the full catalog and still benefits from tool search here. 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.
364
366
 
365
- > 🔄 **Refresh your client's tool list after changing this (or any) setting.** Toggling `ENABLE_TOOL_SEARCH` (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its **cached** tool list until it re-fetches. Restarting the app or Home Assistant does **not** refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but return `Unknown tool` when called. ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a **different name**.
367
+ > 🔄 **Refresh your client's tool list after changing this (or any) setting.** Toggling `ENABLE_TOOL_SEARCH` (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its **cached** tool list until it re-fetches. Restarting the app or Home Assistant does **not** refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but fail when called (`Unknown tool`, or the Read Only Mode error for a write tool that mode hides). ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a **different name**.
366
368
 
367
369
  For the HA app, the same option is documented in [`homeassistant-addon/DOCS.md`](homeassistant-addon/DOCS.md#enable_tool_search) along with the in-app settings UI for fine-grained tool enable/disable/pin.
368
370
 
@@ -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.dev2548"
7
+ version = "8.4.3.dev2552"
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
@@ -9,6 +9,12 @@ Tools are categorized by their existing MCP annotations:
9
9
  - readOnlyHint=True → "read" category
10
10
  - destructiveHint=True with remove/delete in name → "delete" category
11
11
  - destructiveHint=True (other) → "write" category
12
+
13
+ A ``manage`` tool combines several operations behind one name, so it is
14
+ reachable from every proxy — read-approved calls only on the read proxy,
15
+ the whole tool on the write and delete proxies (see ``_admits``) — and
16
+ search results point each kind of action at its own proxy. In Read Only
17
+ Mode only the read proxy is listed.
12
18
  """
13
19
 
14
20
  from __future__ import annotations
@@ -85,6 +91,12 @@ DEFAULT_PINNED_TOOLS: tuple[str, ...] = (
85
91
  # Tool name patterns that indicate delete/remove operations
86
92
  _DELETE_PATTERNS = ("_remove_", "_delete_")
87
93
 
94
+ # ``manage`` names one interface that intentionally combines several
95
+ # operations (.gemini/styleguide.md, Tool Naming Convention). Such a tool is
96
+ # categorised by its annotations like any other, but every call proxy can
97
+ # reach it — see ``_admits``.
98
+ _MANAGE_PATTERNS = ("_manage_",)
99
+
88
100
  # Capability tier a tool falls into — shared by the call-proxy routing and
89
101
  # the settings-UI capability badges. See ``categorize_capability``.
90
102
  Capability = Literal["read", "write", "delete"]
@@ -192,22 +204,26 @@ def categorize_capability(
192
204
  return "write"
193
205
 
194
206
 
207
+ def _is_manage_tool(name: str) -> bool:
208
+ """Whether *name* follows the ``manage`` convention for a multi-operation tool."""
209
+ return any(pattern in name for pattern in _MANAGE_PATTERNS)
210
+
211
+
195
212
  def _is_read_call_on_write_tool(name: str, arguments: dict[str, Any] | None) -> bool:
196
213
  """Whether this call on a non-read-category tool is one of its read actions.
197
214
 
198
215
  A mixed read/write tool (``ha_manage_backup``, ``ha_manage_updates``,
199
- ``ha_manage_blueprints``, ...) is categorised ``write`` by its annotations,
200
- which is where it stays: it is advertised under the write proxy and appears
201
- in no other category set. But refusing its list/get actions on the read
202
- proxy would strip real read surface from a client that only holds that
203
- proxy — for blueprints, folding the old read tool into the merged one would
204
- otherwise have removed listing entirely (#2329).
205
-
206
- So membership decides advertisement, and this decides admission: read-only
207
- mode already enumerates, per call, which invocations of such a tool are
208
- reads (``READ_ONLY_EXEMPT_TOOLS``), and the read proxy reuses that verdict
209
- so the two surfaces cannot disagree about what counts as a read. A call
210
- that changes state is still refused here.
216
+ ``ha_manage_blueprints``, ...) is categorised ``write`` by its annotations
217
+ and lives in that category set only. But refusing its list/get actions on
218
+ the read proxy would strip real read surface from a client that only holds
219
+ that proxy — for blueprints, folding the old read tool into the merged one
220
+ would otherwise have removed listing entirely (#2329).
221
+
222
+ Read-only mode already enumerates, per call, which invocations of such a
223
+ tool are reads (``READ_ONLY_EXEMPT_TOOLS``), and the proxies reuse that
224
+ verdict so the two surfaces cannot disagree about what counts as a read.
225
+ The verdict fails closed: a tool without an exemption entry, or a missing
226
+ or unknown action, is never a read.
211
227
  """
212
228
  from ..read_only import READ_ONLY_EXEMPT_TOOLS
213
229
 
@@ -215,6 +231,74 @@ def _is_read_call_on_write_tool(name: str, arguments: dict[str, Any] | None) ->
215
231
  return exemption is not None and exemption.blocked_write(arguments or {}) is None
216
232
 
217
233
 
234
+ def _has_read_actions(name: str) -> bool:
235
+ """Whether a write-category tool has read actions the read proxy can approve."""
236
+ from ..read_only import READ_ONLY_EXEMPT_TOOLS
237
+
238
+ return name in READ_ONLY_EXEMPT_TOOLS
239
+
240
+
241
+ def _admits(
242
+ transform: CategorizedSearchTransform,
243
+ category: Capability,
244
+ name: str,
245
+ arguments: dict[str, Any] | None,
246
+ ) -> bool:
247
+ """Whether the *category* proxy may dispatch this call.
248
+
249
+ Membership in the category set is the rule. A ``manage`` tool (#2358) is
250
+ the exception: it is reachable from every proxy. The read proxy is the
251
+ one hard boundary — it carries ``readOnlyHint`` — so it admits only a
252
+ call the read-only predicate approves. The write and delete proxies both
253
+ carry ``destructiveHint`` and run the whole tool: its delete actions are
254
+ not statically enumerable (``ha_manage_radio`` and ``ha_manage_updates``
255
+ take a free-form ``action``), so neither proxy pretends to carve them out.
256
+ """
257
+ if category == "read":
258
+ return name in transform._read_tools or (
259
+ name in transform._write_tools
260
+ and _is_read_call_on_write_tool(name, arguments)
261
+ )
262
+ if category == "write":
263
+ return name in transform._write_tools
264
+ return name in transform._delete_tools or (
265
+ name in transform._write_tools and _is_manage_tool(name)
266
+ )
267
+
268
+
269
+ def _execute_via(proxy: str, tool_name: str) -> str:
270
+ """Render the call form a search result advertises for *tool_name*."""
271
+ return (
272
+ f'client.{proxy}(name="{tool_name}", arguments={{...}}) '
273
+ f'or {proxy}(name="{tool_name}", arguments={{...}})'
274
+ )
275
+
276
+
277
+ def _read_only_mode() -> bool:
278
+ """Whether Read Only Mode is on — consulted per request, like its filter."""
279
+ from ..config import get_global_settings
280
+
281
+ return bool(get_global_settings().read_only_mode)
282
+
283
+
284
+ def _advertised_routes(name: str, category: Capability) -> list[Capability]:
285
+ """Proxies a search result points at for *name*, in listing order.
286
+
287
+ A manage tool lists every proxy it is reachable through (see
288
+ ``_admits``); the read route exists only when the read-only predicate can
289
+ approve calls to it. In Read Only Mode the destructive proxies are not
290
+ listed, so only the read route remains. The ``["write", "delete"]`` case
291
+ cannot occur in that mode: ``ReadOnlyToolsTransform`` drops every
292
+ non-exempt write tool from the catalog before the search index is built,
293
+ and every exempt tool has read actions.
294
+ """
295
+ if category != "write" or not _is_manage_tool(name):
296
+ return [category]
297
+ if _has_read_actions(name):
298
+ return ["read"] if _read_only_mode() else ["read", "write", "delete"]
299
+ return ["write", "delete"]
300
+
301
+
218
302
  def _categorize_tool(tool: Tool) -> Capability:
219
303
  """Categorize a Tool as read, write, or delete based on annotations and name."""
220
304
  annotations = tool.annotations
@@ -443,11 +527,15 @@ class CategorizedSearchTransform(BM25SearchTransform):
443
527
  results = []
444
528
  for tool in tools:
445
529
  data = tool.to_mcp_tool().model_dump(mode="json", exclude_none=True)
446
- proxy = proxy_map[_categorize_tool(tool)]
447
- data["execute_via"] = (
448
- f'client.{proxy}(name="{tool.name}", arguments={{...}}) '
449
- f'or {proxy}(name="{tool.name}", arguments={{...}})'
450
- )
530
+ routes = _advertised_routes(tool.name, _categorize_tool(tool))
531
+ if len(routes) == 1:
532
+ data["execute_via"] = _execute_via(proxy_map[routes[0]], tool.name)
533
+ else:
534
+ hint = "; ".join(
535
+ f"{route} actions: {_execute_via(proxy_map[route], tool.name)}"
536
+ for route in routes
537
+ )
538
+ data["execute_via"] = hint[:1].upper() + hint[1:]
451
539
  results.append(data)
452
540
  return results
453
541
 
@@ -487,14 +575,6 @@ class CategorizedSearchTransform(BM25SearchTransform):
487
575
  # needs the action its old signature never carried.
488
576
  arguments = adapt_retired_arguments(requested, arguments)
489
577
 
490
- # Determine which category set to check
491
- if category == "read":
492
- allowed = transform._read_tools
493
- elif category == "delete":
494
- allowed = transform._delete_tools
495
- else:
496
- allowed = transform._write_tools
497
-
498
578
  # Detect and unwrap double-wrapped arguments where the LLM
499
579
  # accidentally nested name/arguments inside the arguments param
500
580
  # e.g. ha_call_read_tool(name="ha_call_read_tool",
@@ -540,11 +620,7 @@ class CategorizedSearchTransform(BM25SearchTransform):
540
620
  _raise_non_object_arguments(nested, proxy_name, inner_name)
541
621
  arguments = adapt_retired_arguments(requested_inner, nested or {})
542
622
 
543
- # Membership is the advertised category; a mixed tool's read
544
- # actions are additionally admitted here (see the helper).
545
- if name not in allowed and not (
546
- category == "read" and _is_read_call_on_write_tool(name, arguments)
547
- ):
623
+ if not _admits(transform, category, name, arguments):
548
624
  _raise_wrong_category_error(name, transform, proxy_name)
549
625
 
550
626
  return await ctx.fastmcp.call_tool(name, arguments)
@@ -590,6 +666,16 @@ class CategorizedSearchTransform(BM25SearchTransform):
590
666
  description=self._proxy_descs["delete"],
591
667
  )
592
668
 
669
+ if _read_only_mode():
670
+ # ReadOnlyToolsTransform runs before this one and never sees the
671
+ # proxies synthesised here; with every write blocked at call time
672
+ # the destructive proxies would only advertise dead ends. They
673
+ # stay resolvable by name so a client holding a stale catalog gets
674
+ # the proxy's own structured answer (ReadOnlyMiddleware blocks a
675
+ # write before the proxy is even resolved) rather than a bare
676
+ # not-found that ToolSearchHintMiddleware declines to explain
677
+ # while tool search is on.
678
+ return [*pinned, search_tool, call_read]
593
679
  return [*pinned, search_tool, call_read, call_write, call_delete]
594
680
 
595
681
  async def get_tool(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 8.4.3.dev2548
3
+ Version: 8.4.3.dev2552
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
@@ -380,6 +380,8 @@ Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA app). The full catal
380
380
 
381
381
  The proxy split lets MCP clients apply different permission policies per category (e.g. auto-approve reads, prompt for writes, confirm deletes) without parsing tool docstrings.
382
382
 
383
+ A `ha_manage_*` tool combines several operations, so it is reachable from more than one proxy: `ha_call_write_tool` and `ha_call_delete_tool` run the whole tool, while `ha_call_read_tool` runs only the read actions Read Only Mode approves (the same per-call verdict), so a manage tool with no approved read actions is not reachable there. Search results list each proxy the tool can be reached through. In Read Only Mode only `ha_call_read_tool` is listed; the other two still answer a client that holds them in a cached tool list, and any write they carry gets the Read Only Mode error rather than `Unknown tool`.
384
+
383
385
  | Setting | Default | Description |
384
386
  |---------|---------|-------------|
385
387
  | `ENABLE_TOOL_SEARCH` | `false` | Replace full tool catalog with search-based discovery (tools deferred behind on-demand search). |
@@ -394,7 +396,7 @@ The proxy split lets MCP clients apply different permission policies per categor
394
396
 
395
397
  Leave it off in clients with deferred tool loading (claude.ai, Claude Desktop, Claude Code); the full catalog has no idle cost there, direct calls skip the search step, and the client's built-in tool search is the better choice — there is no benefit to running ha-mcp's on top of it. Whether tools are deferred depends on the client and model combination: the same model can behave differently per client — GitHub Copilot CLI running Claude Sonnet/Opus inlines the full catalog and still benefits from tool search here. 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.
396
398
 
397
- > 🔄 **Refresh your client's tool list after changing this (or any) setting.** Toggling `ENABLE_TOOL_SEARCH` (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its **cached** tool list until it re-fetches. Restarting the app or Home Assistant does **not** refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but return `Unknown tool` when called. ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a **different name**.
399
+ > 🔄 **Refresh your client's tool list after changing this (or any) setting.** Toggling `ENABLE_TOOL_SEARCH` (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its **cached** tool list until it re-fetches. Restarting the app or Home Assistant does **not** refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but fail when called (`Unknown tool`, or the Read Only Mode error for a write tool that mode hides). ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a **different name**.
398
400
 
399
401
  For the HA app, the same option is documented in [`homeassistant-addon/DOCS.md`](homeassistant-addon/DOCS.md#enable_tool_search) along with the in-app settings UI for fine-grained tool enable/disable/pin.
400
402