ha-mcp-dev 8.3.0.dev2385__tar.gz → 8.3.0.dev2387__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 (276) hide show
  1. {ha_mcp_dev-8.3.0.dev2385/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.3.0.dev2387}/PKG-INFO +1 -1
  2. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/pyproject.toml +1 -1
  3. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_addons.py +285 -66
  4. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/LICENSE +0 -0
  6. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/README.md +0 -0
  8. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/setup.cfg +0 -0
  9. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_rich_compat.py +0 -0
  13. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/__init__.py +0 -0
  14. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/requirements.txt +0 -0
  15. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
  16. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
  17. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
  18. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
  19. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
  20. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
  21. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
  22. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
  23. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
  24. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
  25. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
  26. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
  27. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
  28. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/client.py +0 -0
  29. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
  30. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
  31. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
  32. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
  33. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
  34. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
  35. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
  36. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
  37. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
  38. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
  39. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
  40. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
  41. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
  42. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
  43. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
  44. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
  45. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
  46. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
  47. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
  48. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
  49. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
  50. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
  51. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/server.py +0 -0
  52. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
  53. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
  54. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
  55. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
  56. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
  57. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
  58. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
  59. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
  60. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
  61. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
  62. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
  63. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
  64. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
  65. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
  66. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
  67. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
  68. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
  69. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
  70. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_vendor/websockets/version.py +0 -0
  71. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/_version.py +0 -0
  72. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/auth/__init__.py +0 -0
  73. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/auth/consent_form.py +0 -0
  74. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/auth/oidc_compat.py +0 -0
  75. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/auth/provider.py +0 -0
  76. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/backup_manager.py +0 -0
  77. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/browser_landing.py +0 -0
  78. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/client/__init__.py +0 -0
  79. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/client/rest_client.py +0 -0
  80. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/client/supervisor_client.py +0 -0
  81. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/client/websocket_client.py +0 -0
  82. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/client/websocket_listener.py +0 -0
  83. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/config.py +0 -0
  84. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  85. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  86. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  87. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  88. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  89. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  90. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/errors.py +0 -0
  91. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/hacs_auto_refresh.py +0 -0
  92. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/llm_exposure.py +0 -0
  93. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/__init__.py +0 -0
  94. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/approval_queue.py +0 -0
  95. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/editing.py +0 -0
  96. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/evaluator.py +0 -0
  97. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/handlers.py +0 -0
  98. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/middleware.py +0 -0
  99. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/model.py +0 -0
  100. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/persistence.py +0 -0
  101. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/policy/value_sources.py +0 -0
  102. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/py.typed +0 -0
  103. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/read_only.py +0 -0
  104. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/redaction.py +0 -0
  105. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/renamed_tools.py +0 -0
  106. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  107. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  108. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  109. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  110. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  111. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  112. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  113. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  114. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  115. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  116. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  117. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  118. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  119. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  120. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  121. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  122. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  123. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  124. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  125. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  126. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  127. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  128. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  129. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  130. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  131. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/server.py +0 -0
  132. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/__init__.py +0 -0
  133. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  134. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  135. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  136. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  137. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  138. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  139. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  140. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  141. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  142. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_theme.py +0 -0
  143. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  144. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  145. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
  146. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  147. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/en.json +0 -0
  148. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
  149. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/es.json +0 -0
  150. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
  151. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/it.json +0 -0
  152. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
  153. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
  154. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  155. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
  156. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
  157. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/settings.css +0 -0
  158. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/settings.html +0 -0
  159. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/settings_ui/settings.js +0 -0
  160. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/smoke_test.py +0 -0
  161. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  162. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/strict_bps.py +0 -0
  163. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/__init__.py +0 -0
  164. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/auto_backup.py +0 -0
  165. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/backup.py +0 -0
  166. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  167. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/best_practice_result.py +0 -0
  168. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/best_practice_variables.py +0 -0
  169. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/component_api.py +0 -0
  170. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/component_config_entries.py +0 -0
  171. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/component_config_reads.py +0 -0
  172. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/component_devices.py +0 -0
  173. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/component_registries.py +0 -0
  174. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  175. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  176. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
  177. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
  178. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
  179. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_identity.py +0 -0
  180. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
  181. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
  182. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/device_control.py +0 -0
  183. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/error_log_parsing.py +0 -0
  184. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/hacs_registration.py +0 -0
  185. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/helpers.py +0 -0
  186. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
  187. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/radio/__init__.py +0 -0
  188. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/radio/base.py +0 -0
  189. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/radio/matter.py +0 -0
  190. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/radio/thread.py +0 -0
  191. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  192. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/radio/zwave.py +0 -0
  193. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/reference_validator.py +0 -0
  194. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/registry.py +0 -0
  195. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
  196. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  197. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  198. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  199. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  200. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  201. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  202. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  203. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  204. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  205. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  206. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_areas.py +0 -0
  207. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
  208. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  209. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  210. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_calendar.py +0 -0
  211. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_camera.py +0 -0
  212. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_categories.py +0 -0
  213. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_code.py +0 -0
  214. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  215. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  216. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  217. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  218. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  219. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  220. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_dev.py +0 -0
  221. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_energy.py +0 -0
  222. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_entities.py +0 -0
  223. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  224. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_groups.py +0 -0
  225. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_hacs.py +0 -0
  226. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_history.py +0 -0
  227. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_integrations.py +0 -0
  228. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_labels.py +0 -0
  229. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_radio.py +0 -0
  230. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_registry.py +0 -0
  231. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_resources.py +0 -0
  232. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_search.py +0 -0
  233. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_security_policy.py +0 -0
  234. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_service.py +0 -0
  235. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_services.py +0 -0
  236. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_system.py +0 -0
  237. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_themes.py +0 -0
  238. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_todo.py +0 -0
  239. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_traces.py +0 -0
  240. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_updates.py +0 -0
  241. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_utility.py +0 -0
  242. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  243. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  244. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  245. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/tools_zones.py +0 -0
  246. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/util_helpers.py +0 -0
  247. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/tools/validation_middleware.py +0 -0
  248. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/transforms/__init__.py +0 -0
  249. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/transforms/categorized_search.py +0 -0
  250. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  251. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/transport_security.py +0 -0
  252. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/update_check.py +0 -0
  253. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/__init__.py +0 -0
  254. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/config_hash.py +0 -0
  255. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/config_write_lock.py +0 -0
  256. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/data_paths.py +0 -0
  257. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/domain_handlers.py +0 -0
  258. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  259. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  260. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/operation_manager.py +0 -0
  261. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/python_sandbox.py +0 -0
  262. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/skill_loader.py +0 -0
  263. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/utils/usage_logger.py +0 -0
  264. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/visibility/__init__.py +0 -0
  265. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/visibility/enforcement.py +0 -0
  266. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/visibility/model.py +0 -0
  267. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/visibility/persistence.py +0 -0
  268. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp/visibility/resolver.py +0 -0
  269. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  270. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  271. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  272. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  273. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  274. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/tests/__init__.py +0 -0
  275. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/tests/test_constants.py +0 -0
  276. {ha_mcp_dev-8.3.0.dev2385 → ha_mcp_dev-8.3.0.dev2387}/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.3.0.dev2385
3
+ Version: 8.3.0.dev2387
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.3.0.dev2385"
7
+ version = "8.3.0.dev2387"
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
@@ -11,6 +11,7 @@ import asyncio
11
11
  import json
12
12
  import logging
13
13
  import re
14
+ import ssl
14
15
  import time
15
16
  from typing import Annotated, Any, ClassVar, Literal, NoReturn
16
17
  from urllib.parse import unquote, urlsplit
@@ -23,7 +24,11 @@ from ha_mcp._vendor import websockets
23
24
  from ha_mcp._vendor.websockets.asyncio.client import ClientConnection
24
25
 
25
26
  from .._version import is_running_in_addon
26
- from ..client.rest_client import HomeAssistantClient, HomeAssistantCommandError
27
+ from ..client.rest_client import (
28
+ HomeAssistantClient,
29
+ HomeAssistantCommandError,
30
+ _is_ssl_error,
31
+ )
27
32
  from ..errors import (
28
33
  ErrorCode,
29
34
  create_error_response,
@@ -892,6 +897,111 @@ async def _collect_ws_messages_loop(
892
897
  total_size += len(clean)
893
898
 
894
899
 
900
+ def _tls_setup_error(e: Exception, slug: str) -> NoReturn:
901
+ """Report a CA-store/TLS-context build failure distinctly from network errors."""
902
+ raise_tool_error(
903
+ create_error_response(
904
+ ErrorCode.CONNECTION_FAILED,
905
+ f"Could not build a TLS context for the app (add-on) request: {e!s}",
906
+ context={"slug": slug},
907
+ suggestions=[
908
+ "The MCP host's CA store failed to load. Check SSL_CERT_FILE "
909
+ "and SSL_CERT_DIR, or the system certificate bundle.",
910
+ ],
911
+ )
912
+ )
913
+
914
+
915
+ def _build_ws_ssl_context(
916
+ ws_url: str, verify_ssl: bool, slug: str
917
+ ) -> ssl.SSLContext | None:
918
+ """Build the proxy's client TLS context, honoring the HA verify setting."""
919
+ if not ws_url.startswith("wss://"):
920
+ return None
921
+ try:
922
+ ssl_context = ssl.create_default_context()
923
+ except OSError as e:
924
+ _tls_setup_error(e, slug)
925
+ if not verify_ssl:
926
+ logger.warning(
927
+ "TLS verification disabled for add-on WebSocket proxy "
928
+ "(HA_VERIFY_SSL=false). Connecting to %s with hostname/cert "
929
+ "checks off.",
930
+ ws_url,
931
+ )
932
+ ssl_context.check_hostname = False
933
+ ssl_context.verify_mode = ssl.CERT_NONE
934
+ return ssl_context
935
+
936
+
937
+ def _tls_verification_failure_suggestions() -> list[str]:
938
+ """Name the TLS remedy instead of pointing the caller at the network."""
939
+ return [
940
+ "Home Assistant's certificate did not validate. If it is self-signed "
941
+ "or issued for a different hostname than the configured HA URL, set "
942
+ "HA_VERIFY_SSL=false to skip verification, or reach HA at the "
943
+ "hostname the certificate was issued for.",
944
+ ]
945
+
946
+
947
+ def _build_addon_http_client(
948
+ client: HomeAssistantClient, timeout: int, slug: str
949
+ ) -> httpx.AsyncClient:
950
+ """Build the proxy's HTTP client, honoring the HA verify setting.
951
+
952
+ httpx builds its TLS context at construction; doing it before the request
953
+ try reports a CA-store failure as such rather than letting it escape
954
+ unstructured.
955
+ """
956
+ try:
957
+ return httpx.AsyncClient(
958
+ timeout=httpx.Timeout(timeout),
959
+ verify=client.verify_ssl,
960
+ )
961
+ except OSError as e:
962
+ _tls_setup_error(e, slug)
963
+ return None # py/mixed-returns: unreachable, _tls_setup_error raises
964
+
965
+
966
+ def _raise_connect_failure(
967
+ client: HomeAssistantClient,
968
+ e: Exception,
969
+ *,
970
+ label: str,
971
+ url: str,
972
+ slug: str,
973
+ port: int | None,
974
+ ) -> NoReturn:
975
+ """Raise the structured connect-phase error, classifying TLS failures.
976
+
977
+ A certificate-verification failure with verification enabled gets the TLS
978
+ remedy; everything else keeps the route-appropriate network guidance.
979
+ """
980
+ if _is_ssl_error(e) and client.verify_ssl:
981
+ raise_tool_error(
982
+ create_error_response(
983
+ ErrorCode.CONNECTION_FAILED,
984
+ f"TLS verification failed connecting to {label}: {e!s}",
985
+ details=f"url={url}",
986
+ context={
987
+ "slug": slug,
988
+ "direct_port": bool(port),
989
+ "verify_ssl": True,
990
+ },
991
+ suggestions=_tls_verification_failure_suggestions(),
992
+ )
993
+ )
994
+ raise_tool_error(
995
+ create_error_response(
996
+ ErrorCode.CONNECTION_FAILED,
997
+ f"Failed to connect to {label}: {e!s}",
998
+ details=f"url={url}",
999
+ context={"slug": slug, "direct_port": bool(port)},
1000
+ suggestions=_addon_connection_failure_suggestions(client, port),
1001
+ )
1002
+ )
1003
+
1004
+
895
1005
  async def _run_ws_session(
896
1006
  ws_url: str,
897
1007
  headers: dict[str, str],
@@ -900,6 +1010,7 @@ async def _run_ws_session(
900
1010
  timeout: int,
901
1011
  wait_for_close: bool,
902
1012
  caller_capped: bool,
1013
+ ssl_context: ssl.SSLContext | None,
903
1014
  ) -> tuple[list[str], int, str, float]:
904
1015
  """Connect to a WebSocket URL, optionally send body, collect messages.
905
1016
 
@@ -916,6 +1027,7 @@ async def _run_ws_session(
916
1027
  max_size=5 * 1024 * 1024, # 5MB max per message
917
1028
  open_timeout=10,
918
1029
  close_timeout=5,
1030
+ ssl=ssl_context,
919
1031
  ) as ws:
920
1032
  if body is not None:
921
1033
  await ws.send(json.dumps(body) if isinstance(body, dict) else str(body))
@@ -1024,6 +1136,58 @@ def _build_ws_result(
1024
1136
  return result
1025
1137
 
1026
1138
 
1139
+ def _front_door_state(addon: dict[str, Any]) -> bool | None:
1140
+ """Return the app's ``leave_front_door_open`` state, or None when unexposed.
1141
+
1142
+ Supervisor's ``options`` dict omits an option the user never saved even
1143
+ when the app's schema exposes it, and the app treats that absence as
1144
+ disabled — so the schema, not key presence, decides whether the option
1145
+ applies (stock installs ship the key in the schema only).
1146
+ """
1147
+ options = addon.get("options")
1148
+ if isinstance(options, dict) and "leave_front_door_open" in options:
1149
+ return bool(options["leave_front_door_open"])
1150
+ schema = addon.get("schema")
1151
+ if isinstance(schema, list) and any(
1152
+ isinstance(item, dict) and item.get("name") == "leave_front_door_open"
1153
+ for item in schema
1154
+ ):
1155
+ return False
1156
+ return None
1157
+
1158
+
1159
+ def _ws_auth_error_suggestions(
1160
+ addon: dict[str, Any], slug: str, port: int | None, status: int
1161
+ ) -> list[str]:
1162
+ """Return route-appropriate guidance for a rejected WS handshake."""
1163
+ if port:
1164
+ # Prefer the caller-resolved slug; fall back to the addon dict, then a
1165
+ # placeholder, so the suggestion never renders slug=''.
1166
+ slug_val = slug or addon.get("slug") or "<slug>"
1167
+ if _front_door_state(addon) is False:
1168
+ primary = _direct_port_auth_suggestion(slug_val)
1169
+ else:
1170
+ primary = _direct_port_rejection_suggestion()
1171
+ elif is_running_in_addon():
1172
+ # The addon-variant route authenticates with Supervisor ingress
1173
+ # headers, not an ingress session or HA token — do not send the
1174
+ # caller to inspect credentials this request never carried.
1175
+ primary = (
1176
+ "The app (add-on) rejected the ingress-port handshake. This route "
1177
+ "authenticates with Supervisor ingress headers; check that the "
1178
+ "target app is running and its ingress is healthy."
1179
+ )
1180
+ else:
1181
+ primary = (
1182
+ "The ingress session may have expired or your HA token may lack the "
1183
+ "required scope. Verify the token has admin rights and try again."
1184
+ )
1185
+ return [
1186
+ primary,
1187
+ f"Status {status} from the WebSocket handshake.",
1188
+ ]
1189
+
1190
+
1027
1191
  async def _call_addon_ws(
1028
1192
  client: HomeAssistantClient,
1029
1193
  slug: str,
@@ -1115,6 +1279,9 @@ async def _call_addon_ws(
1115
1279
  requested = max(0, message_offset) + max(0, message_limit)
1116
1280
  collection_cap = min(_MAX_WS_MESSAGES, requested)
1117
1281
 
1282
+ # Built before the try so a CA-store failure is reported as such rather
1283
+ # than as the add-on being unreachable.
1284
+ ssl_context = _build_ws_ssl_context(ws_url, client.verify_ssl, slug)
1118
1285
  try:
1119
1286
  collected, total_size, close_reason, elapsed = await _run_ws_session(
1120
1287
  ws_url,
@@ -1124,6 +1291,7 @@ async def _call_addon_ws(
1124
1291
  timeout,
1125
1292
  wait_for_close,
1126
1293
  caller_capped=message_limit is not None,
1294
+ ssl_context=ssl_context,
1127
1295
  )
1128
1296
  except websockets.exceptions.InvalidHandshake as e:
1129
1297
  suggestions = [
@@ -1134,12 +1302,7 @@ async def _call_addon_ws(
1134
1302
  if isinstance(e, websockets.exceptions.InvalidStatus):
1135
1303
  status = e.response.status_code
1136
1304
  if status in (401, 403):
1137
- suggestions = [
1138
- "The ingress session may have expired or your HA token "
1139
- "may lack the required scope. Verify the token has admin "
1140
- "rights and try again.",
1141
- f"Status {status} from the WebSocket handshake.",
1142
- ]
1305
+ suggestions = _ws_auth_error_suggestions(addon, slug, port, status)
1143
1306
  raise_tool_error(
1144
1307
  create_error_response(
1145
1308
  ErrorCode.SERVICE_CALL_FAILED,
@@ -1177,14 +1340,13 @@ async def _call_addon_ws(
1177
1340
  )
1178
1341
  )
1179
1342
  except OSError as e:
1180
- raise_tool_error(
1181
- create_error_response(
1182
- ErrorCode.CONNECTION_FAILED,
1183
- f"Failed to connect to add-on '{addon_name}' WebSocket: {e!s}",
1184
- details=f"url={ws_url}",
1185
- context={"slug": slug, "direct_port": bool(port)},
1186
- suggestions=_addon_connection_failure_suggestions(client, port),
1187
- )
1343
+ _raise_connect_failure(
1344
+ client,
1345
+ e,
1346
+ label=f"app (add-on) '{addon_name}' WebSocket",
1347
+ url=ws_url,
1348
+ slug=slug,
1349
+ port=port,
1188
1350
  )
1189
1351
 
1190
1352
  return _build_ws_result(
@@ -1553,57 +1715,106 @@ def _build_http_result(
1553
1715
  return result
1554
1716
 
1555
1717
 
1718
+ def _addon_config_for_http_hint(addon: dict[str, Any]) -> dict[str, Any]:
1719
+ """Return the app settings that can explain direct-access failures."""
1720
+ return {
1721
+ "options": addon.get("options"),
1722
+ "ports": addon.get("network") or addon.get("ports") or None,
1723
+ "host_network": addon.get("host_network"),
1724
+ "ingress_port": addon.get("ingress_port"),
1725
+ }
1726
+
1727
+
1728
+ def _direct_port_auth_suggestion(slug: str) -> str:
1729
+ """Explain the configurable direct-access auth trade-off, Ingress first."""
1730
+ return (
1731
+ "The app (add-on) rejected this direct-port request. Prefer Ingress: retry "
1732
+ f"without the 'port' parameter (ha_manage_app(slug='{slug}', "
1733
+ "path='...')). The app's 'leave_front_door_open' option controls "
1734
+ "direct-access authentication; only if the user explicitly accepts "
1735
+ "the security trade-off, use "
1736
+ f"ha_manage_app(slug='{slug}', "
1737
+ "options={'leave_front_door_open': True}), then "
1738
+ f"ha_manage_app(slug='{slug}', action='restart'), and retry. Enabling "
1739
+ "it removes authentication from the app's direct-access surface for "
1740
+ "hosts that can reach the mapped port."
1741
+ )
1742
+
1743
+
1744
+ def _direct_port_rejection_suggestion() -> str:
1745
+ """Explain a direct-port rejection the front-door option cannot fix.
1746
+
1747
+ Fires when the app exposes no ``leave_front_door_open`` option, or when it
1748
+ is already enabled — either way the remedy lives in the app's own auth
1749
+ configuration, not in that option.
1750
+ """
1751
+ return (
1752
+ "The app (add-on) rejected this direct-port request. Check the app's own "
1753
+ "authentication and access-control settings, IP allowlist, and logs."
1754
+ )
1755
+
1756
+
1556
1757
  def _add_http_error_hints(
1557
1758
  result: dict[str, Any],
1558
1759
  response: httpx.Response,
1559
1760
  addon: dict[str, Any],
1560
1761
  slug: str,
1762
+ direct_port: bool,
1561
1763
  ) -> None:
1562
1764
  """Mutate result to add an error key for 4xx/5xx responses, with tailored suggestions for 401 and 403."""
1563
1765
  if response.status_code >= 400:
1564
1766
  result["error"] = f"Add-on API returned HTTP {response.status_code}"
1767
+ # Prefer the caller-resolved slug (authoritative); fall back to the
1768
+ # addon dict, then a placeholder only if neither is populated.
1769
+ slug_val = slug or addon.get("slug") or "<slug>"
1565
1770
  if response.status_code == 401:
1566
- # 401 is a credential/session problem — addon_config is not attached
1567
- # because the network layout is irrelevant; the caller needs to fix
1568
- # their token or re-establish the ingress session, not reconfigure ports.
1569
- result["suggestion"] = (
1570
- "Authentication failed. The ingress session may have expired, "
1571
- "or your HA token may lack the required scope. Verify the "
1572
- "token has admin rights and try again."
1573
- )
1574
- elif response.status_code == 403:
1575
- # 403 is typically an Nginx IP ACL blocking direct access — a
1576
- # network configuration problem. Attach addon_config so the LLM
1577
- # can see the port mapping and suggest the correct port override.
1578
- ports_dict = addon.get("network") or addon.get("ports") or {}
1579
- unmapped = sorted(k for k, v in ports_dict.items() if v is None)
1580
- result["addon_config"] = {
1581
- "options": addon.get("options"),
1582
- "ports": ports_dict or None,
1583
- "host_network": addon.get("host_network"),
1584
- "ingress_port": addon.get("ingress_port"),
1585
- }
1586
- # Prefer the caller-resolved slug (authoritative); fall back to the
1587
- # addon dict, then a placeholder only if neither is populated.
1588
- slug_val = slug or addon.get("slug") or "<slug>"
1589
- example_proto = unmapped[0] if unmapped else ""
1590
- example_port = example_proto.split("/", 1)[0] if example_proto else ""
1591
- if unmapped and example_port.isdigit():
1592
- addon_label = addon.get("name") or slug_val
1593
- result["suggestion"] = (
1594
- f"Map {example_proto} to a host port in the HA UI "
1595
- f"('{addon_label}' → Configuration → Network), restart the "
1596
- f"add-on, then retry with ha_manage_app(slug='{slug_val}', "
1597
- f"path='...', port={example_port})."
1598
- )
1771
+ if direct_port:
1772
+ result["addon_config"] = _addon_config_for_http_hint(addon)
1773
+ if _front_door_state(addon) is False:
1774
+ result["suggestion"] = _direct_port_auth_suggestion(slug_val)
1775
+ else:
1776
+ result["suggestion"] = _direct_port_rejection_suggestion()
1599
1777
  else:
1778
+ # An ingress 401 is a credential/session problem. Keep network
1779
+ # configuration out of that result so the caller fixes the HA
1780
+ # token or ingress session instead of weakening app authentication.
1600
1781
  result["suggestion"] = (
1601
- "This add-on is blocking direct connections (likely Nginx IP restriction). "
1602
- "Try using the 'port' parameter to connect to the add-on's direct access port "
1603
- "(see addon_config.ports above) with 'leave_front_door_open' enabled. "
1604
- "Example: ha_manage_app(slug='...', path='...', port=<direct_port>). "
1605
- "The user may need to change add-on settings in the HA UI and restart the add-on."
1782
+ "Authentication failed. The ingress session may have expired, "
1783
+ "or your HA token may lack the required scope. Verify the "
1784
+ "token has admin rights and try again."
1606
1785
  )
1786
+ elif response.status_code == 403:
1787
+ # A direct-port 403 is app-level access control; an ingress 403 is
1788
+ # typically an Nginx IP ACL blocking direct access — a network
1789
+ # configuration problem. Attach addon_config either way so the LLM
1790
+ # can see the port mapping.
1791
+ result["addon_config"] = _addon_config_for_http_hint(addon)
1792
+ if direct_port:
1793
+ if _front_door_state(addon) is False:
1794
+ result["suggestion"] = _direct_port_auth_suggestion(slug_val)
1795
+ else:
1796
+ result["suggestion"] = _direct_port_rejection_suggestion()
1797
+ else:
1798
+ ports_dict = addon.get("network") or addon.get("ports") or {}
1799
+ unmapped = sorted(k for k, v in ports_dict.items() if v is None)
1800
+ example_proto = unmapped[0] if unmapped else ""
1801
+ example_port = example_proto.split("/", 1)[0] if example_proto else ""
1802
+ if unmapped and example_port.isdigit():
1803
+ addon_label = addon.get("name") or slug_val
1804
+ result["suggestion"] = (
1805
+ f"Map {example_proto} to a host port in the HA UI "
1806
+ f"('{addon_label}' → Configuration → Network), restart the "
1807
+ f"add-on, then retry with ha_manage_app(slug='{slug_val}', "
1808
+ f"path='...', port={example_port})."
1809
+ )
1810
+ else:
1811
+ result["suggestion"] = (
1812
+ "This add-on is blocking direct connections (likely Nginx IP restriction). "
1813
+ "Try using the 'port' parameter to connect to the add-on's direct access port "
1814
+ "(see addon_config.ports above) with 'leave_front_door_open' enabled. "
1815
+ "Example: ha_manage_app(slug='...', path='...', port=<direct_port>). "
1816
+ "The user may need to change add-on settings in the HA UI and restart the add-on."
1817
+ )
1607
1818
 
1608
1819
 
1609
1820
  async def _call_addon_api(
@@ -1713,8 +1924,9 @@ async def _call_addon_api(
1713
1924
  else:
1714
1925
  request_content = None
1715
1926
 
1927
+ addon_http_client = _build_addon_http_client(client, timeout, slug)
1716
1928
  try:
1717
- async with httpx.AsyncClient(timeout=httpx.Timeout(timeout)) as http_client:
1929
+ async with addon_http_client as http_client:
1718
1930
  response = await http_client.request(
1719
1931
  method=method.upper(),
1720
1932
  url=url,
@@ -1738,14 +1950,13 @@ async def _call_addon_api(
1738
1950
  )
1739
1951
  )
1740
1952
  except httpx.ConnectError as e:
1741
- raise_tool_error(
1742
- create_error_response(
1743
- ErrorCode.CONNECTION_FAILED,
1744
- f"Failed to connect to add-on '{addon_name}': {e!s}",
1745
- details=f"url={url}",
1746
- context={"slug": slug, "direct_port": bool(port)},
1747
- suggestions=_addon_connection_failure_suggestions(client, port),
1748
- )
1953
+ _raise_connect_failure(
1954
+ client,
1955
+ e,
1956
+ label=f"app (add-on) '{addon_name}'",
1957
+ url=url,
1958
+ slug=slug,
1959
+ port=port,
1749
1960
  )
1750
1961
 
1751
1962
  # 6. Parse response body
@@ -1786,7 +1997,7 @@ async def _call_addon_api(
1786
1997
  transformed,
1787
1998
  truncated,
1788
1999
  )
1789
- _add_http_error_hints(result, response, addon, slug)
2000
+ _add_http_error_hints(result, response, addon, slug, direct_port=bool(port))
1790
2001
  return result
1791
2002
 
1792
2003
 
@@ -2884,7 +3095,10 @@ def register_addon_tools(mcp: Any, client: HomeAssistantClient, **kwargs: Any) -
2884
3095
  int | None,
2885
3096
  Field(
2886
3097
  description="Proxy mode only. Connect to this port instead of the Ingress port. "
2887
- "Use ha_get_app(slug='...') to find available ports.",
3098
+ "Use ha_get_app(slug='...') to find available ports. Some apps, including "
3099
+ "Node-RED, reject direct access unless their leave_front_door_open option is "
3100
+ "enabled and the app is restarted; related errors include an actionable, "
3101
+ "security-qualified ha_manage_app options command.",
2888
3102
  default=None,
2889
3103
  ),
2890
3104
  ] = None,
@@ -3086,7 +3300,12 @@ def register_addon_tools(mcp: Any, client: HomeAssistantClient, **kwargs: Any) -
3086
3300
  proxy by default (works on HAOS, Supervised, and off-host PyPI/uvx
3087
3301
  installs). Pass `port=...` to bypass Ingress and connect directly to
3088
3302
  an add-on's container port — that mode requires the MCP host to
3089
- share Home Assistant's container network (i.e. only the HAOS addon).
3303
+ share Home Assistant's container network (in practice: the HAOS app
3304
+ (add-on) deployment). Apps such as Node-RED may reject direct requests
3305
+ unless `leave_front_door_open` is enabled in their options and the app
3306
+ is restarted. Authentication errors name the exact
3307
+ `ha_manage_app(options=...)` remedy and its security tradeoff; prefer
3308
+ Ingress when it works.
3090
3309
 
3091
3310
  **ESPHome Device Builder dashboard (current rewrite):** config and log
3092
3311
  access is a WebSocket JSON-command API, NOT REST. The legacy endpoints
@@ -3148,7 +3367,7 @@ def register_addon_tools(mcp: Any, client: HomeAssistantClient, **kwargs: Any) -
3148
3367
  - Change host port: ha_manage_app(slug="...", network={"5800/tcp": 8082})
3149
3368
  - Set boot mode: ha_manage_app(slug="...", boot="manual")
3150
3369
  - Call HTTP API: ha_manage_app(slug="...", path="/api/events")
3151
- - Direct port: ha_manage_app(slug="...", path="/flows", port=1880)
3370
+ - Direct port: ha_manage_app(slug="...", path="/flows", port=1880) — if the app rejects it, the error names the 'leave_front_door_open' remedy and its security trade-off; prefer Ingress.
3152
3371
  - ESPHome list devices (HTTP): ha_manage_app(slug="<prefix>_esphome", path="/devices")
3153
3372
  - ESPHome read a device's YAML (WS one-shot): ha_manage_app(slug="<prefix>_esphome", path="/ws", websocket=True, wait_for_close=False, message_limit=2, body={"command": "devices/get_config", "message_id": "1", "args": {"configuration": "device.yaml"}})
3154
3373
  - ESPHome live logs (WS, bounded): ha_manage_app(slug="<prefix>_esphome", path="/ws", websocket=True, wait_for_close=False, message_limit=60, body={"command": "devices/logs", "message_id": "1", "args": {"configuration": "device.yaml", "port": "OTA"}})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 8.3.0.dev2385
3
+ Version: 8.3.0.dev2387
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