ha-mcp-dev 7.10.0.dev779__tar.gz → 7.11.0.dev1825__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 (162) hide show
  1. {ha_mcp_dev-7.10.0.dev779/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.11.0.dev1825}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/_version.py +12 -0
  4. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/config.py +16 -0
  5. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/__init__.py +71 -2
  6. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/settings.html +7 -1
  7. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/settings.js +35 -1
  8. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_bug_report.py +104 -10
  9. ha_mcp_dev-7.11.0.dev1825/src/ha_mcp/tools/tools_dev.py +954 -0
  10. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/update_check.py +9 -0
  11. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  12. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  13. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/LICENSE +0 -0
  14. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/MANIFEST.in +0 -0
  15. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/README.md +0 -0
  16. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/setup.cfg +0 -0
  17. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/__init__.py +0 -0
  18. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/__main__.py +0 -0
  19. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/_pypi_marker +0 -0
  20. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/auth/__init__.py +0 -0
  21. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/auth/consent_form.py +0 -0
  22. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/auth/provider.py +0 -0
  23. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/backup_manager.py +0 -0
  24. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/browser_landing.py +0 -0
  25. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/__init__.py +0 -0
  26. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/rest_client.py +0 -0
  27. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/supervisor_client.py +0 -0
  28. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/websocket_client.py +0 -0
  29. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/client/websocket_listener.py +0 -0
  30. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  31. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  32. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  33. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/errors.py +0 -0
  34. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/__init__.py +0 -0
  35. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/approval_queue.py +0 -0
  36. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/evaluator.py +0 -0
  37. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/handlers.py +0 -0
  38. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/middleware.py +0 -0
  39. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/model.py +0 -0
  40. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/persistence.py +0 -0
  41. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/policy/value_sources.py +0 -0
  42. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/py.typed +0 -0
  43. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/read_only.py +0 -0
  44. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  45. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  46. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  47. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  48. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  49. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  50. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  51. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  52. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  53. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  54. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  55. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  56. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  57. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  58. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  59. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  60. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  61. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  62. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  63. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  64. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  65. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  66. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  67. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  68. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  69. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/server.py +0 -0
  70. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/settings_ui/settings.css +0 -0
  71. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/smoke_test.py +0 -0
  72. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  73. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/__init__.py +0 -0
  74. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/auto_backup.py +0 -0
  75. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/backup.py +0 -0
  76. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  77. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  78. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/device_control.py +0 -0
  79. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/enhanced.py +0 -0
  80. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/helpers.py +0 -0
  81. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/__init__.py +0 -0
  82. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/base.py +0 -0
  83. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/matter.py +0 -0
  84. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/thread.py +0 -0
  85. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  86. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/radio/zwave.py +0 -0
  87. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/reference_validator.py +0 -0
  88. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/registry.py +0 -0
  89. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  90. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  91. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  92. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  93. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  94. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  95. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  96. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  97. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  98. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  99. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_addons.py +0 -0
  100. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_areas.py +0 -0
  101. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  102. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_calendar.py +0 -0
  103. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_camera.py +0 -0
  104. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_categories.py +0 -0
  105. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_code.py +0 -0
  106. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  107. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  108. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  109. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  110. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  111. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  112. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_energy.py +0 -0
  113. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_entities.py +0 -0
  114. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  115. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_groups.py +0 -0
  116. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_hacs.py +0 -0
  117. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_history.py +0 -0
  118. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_integrations.py +0 -0
  119. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_labels.py +0 -0
  120. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  121. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_radio.py +0 -0
  122. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_registry.py +0 -0
  123. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_resources.py +0 -0
  124. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_search.py +0 -0
  125. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_service.py +0 -0
  126. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_services.py +0 -0
  127. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_system.py +0 -0
  128. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_themes.py +0 -0
  129. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_todo.py +0 -0
  130. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_traces.py +0 -0
  131. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_updates.py +0 -0
  132. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_utility.py +0 -0
  133. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  134. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  135. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/tools_zones.py +0 -0
  136. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/util_helpers.py +0 -0
  137. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/tools/validation_middleware.py +0 -0
  138. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transforms/__init__.py +0 -0
  139. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transforms/categorized_search.py +0 -0
  140. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  141. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/transport_security.py +0 -0
  142. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/__init__.py +0 -0
  143. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/config_hash.py +0 -0
  144. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/data_paths.py +0 -0
  145. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/domain_handlers.py +0 -0
  146. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  147. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  148. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/operation_manager.py +0 -0
  149. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/python_sandbox.py +0 -0
  150. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/skill_loader.py +0 -0
  151. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/utils/usage_logger.py +0 -0
  152. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/__init__.py +0 -0
  153. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/model.py +0 -0
  154. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/persistence.py +0 -0
  155. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp/visibility/resolver.py +0 -0
  156. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  157. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  158. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  159. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  160. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/tests/__init__.py +0 -0
  161. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/tests/test_constants.py +0 -0
  162. {ha_mcp_dev-7.10.0.dev779 → ha_mcp_dev-7.11.0.dev1825}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.10.0.dev779
3
+ Version: 7.11.0.dev1825
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.10.0.dev779"
7
+ version = "7.11.0.dev1825"
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
@@ -30,6 +30,18 @@ def get_version() -> str:
30
30
  """
31
31
  if override := os.environ.get("HA_MCP_BUILD_VERSION"):
32
32
  return override
33
+ # Prefer the distribution that actually OWNS the installed ha_mcp package.
34
+ # Both channel dists (ha-mcp / ha-mcp-dev) can leave metadata behind — an
35
+ # interrupted channel switch, a best-effort uninstall that failed — and a
36
+ # fixed name order then reports the leftover dist's version instead of the
37
+ # one whose files are really installed.
38
+ try:
39
+ owners = importlib.metadata.packages_distributions().get("ha_mcp", [])
40
+ unique = sorted(set(owners))
41
+ if len(unique) == 1:
42
+ return importlib.metadata.version(unique[0])
43
+ except Exception as exc:
44
+ logger.debug("packages_distributions probe failed: %s", exc)
33
45
  for pkg_name in ("ha-mcp", "ha-mcp-dev"):
34
46
  try:
35
47
  return importlib.metadata.version(pkg_name)
@@ -256,6 +256,15 @@ class Settings(BaseSettings):
256
256
  "", alias="HAMCP_DASHBOARD_SCREENSHOT_ENGINE_URL"
257
257
  )
258
258
 
259
+ # Developer mode (issue #1775) — registers the hidden ha_dev_* tools
260
+ # (server update/restart, direct settings editing). Deliberately NOT a
261
+ # beta flag: it is a development aid, not a feature preview, and must
262
+ # not ride the beta master gate. The toggle renders in its own
263
+ # "Developer" section at the very bottom of the web UI's Server
264
+ # Settings tab; it is intentionally absent from the add-on config
265
+ # schemas so it stays out of the add-on Configuration page.
266
+ enable_dev_mode: bool = Field(False, alias="HAMCP_ENABLE_DEV_MODE")
267
+
259
268
  # Code Mode — sandboxed Python execution via pydantic-monty.
260
269
  # Provides an "escape hatch" tool (ha_manage_custom_tool) that lets LLMs write
261
270
  # custom one-off Python code when no existing tool covers the request.
@@ -556,6 +565,7 @@ AdvancedSection = Literal[
556
565
  "tools_surface",
557
566
  "sidecar",
558
567
  "beta_codemode",
568
+ "developer",
559
569
  ]
560
570
 
561
571
 
@@ -814,6 +824,12 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
814
824
  "beta_codemode",
815
825
  True,
816
826
  ),
827
+ # Developer mode (issue #1775). Lives in ADVANCED_SETTINGS_FIELDS —
828
+ # not FEATURE_FLAG_FIELDS — so it renders in its own "Developer"
829
+ # section at the very bottom of the Server Settings tab instead of
830
+ # among the feature toggles, and stays independent of the beta
831
+ # master gate.
832
+ AdvancedField("enable_dev_mode", "HAMCP_ENABLE_DEV_MODE", bool, "developer", True),
817
833
  )
818
834
 
819
835
 
@@ -27,7 +27,7 @@ import httpx
27
27
  from starlette.requests import Request
28
28
  from starlette.responses import HTMLResponse, JSONResponse, Response
29
29
 
30
- from .._version import get_version, is_running_in_addon
30
+ from .._version import get_version, is_embedded, is_running_in_addon
31
31
  from ..backup_manager import get_backup_manager
32
32
  from ..client.supervisor_client import make_supervisor_httpx_client
33
33
  from ..config import (
@@ -1502,6 +1502,62 @@ def build_settings_handlers(
1502
1502
  )
1503
1503
 
1504
1504
  async def _restart_addon(request: Request) -> JSONResponse:
1505
+ # Embedded (in-process custom-component server): "restart" means
1506
+ # reloading the ha_mcp_tools server config entry. Checked before the
1507
+ # Supervisor path — the HA core container carries SUPERVISOR_TOKEN,
1508
+ # but the Supervisor add-on API is not ours to call from here, and
1509
+ # post-reload the new worker imports the freshly installed code
1510
+ # (the manager purges the module cache per start).
1511
+ if server is not None and is_embedded():
1512
+ from ..tools.tools_dev import (
1513
+ abort_options_flow_quietly,
1514
+ find_server_config_entry,
1515
+ schedule_deferred_entry_reload,
1516
+ )
1517
+
1518
+ try:
1519
+ found = await find_server_config_entry(server.client)
1520
+ except Exception as exc:
1521
+ # A discovery FAILURE is not "entry not found" — masking a
1522
+ # WS/connection hiccup as not-found steers users toward
1523
+ # reinstalling a running component. Also: the restart JS
1524
+ # treats any 5xx as "restart in flight" and starts its
1525
+ # poll-reload cycle, so a restart that was never initiated
1526
+ # must answer BELOW 500.
1527
+ logger.warning("Embedded restart: entry discovery failed: %s", exc)
1528
+ return JSONResponse(
1529
+ create_error_response(
1530
+ ErrorCode.CONNECTION_FAILED,
1531
+ "Could not reach Home Assistant to locate the "
1532
+ f"in-process server's config entry: {exc}",
1533
+ suggestions=[
1534
+ "Retry once Home Assistant is responsive",
1535
+ ],
1536
+ ),
1537
+ status_code=409,
1538
+ )
1539
+ if found is None:
1540
+ return JSONResponse(
1541
+ create_error_response(
1542
+ ErrorCode.INTERNAL_ERROR,
1543
+ "Could not locate the in-process server's config "
1544
+ "entry to reload",
1545
+ suggestions=[
1546
+ "Reload the HA-MCP integration from Settings > "
1547
+ "Devices & Services instead",
1548
+ ],
1549
+ ),
1550
+ # Below 500 on purpose: no restart was initiated, and the
1551
+ # restart JS interprets 5xx as restart-in-flight.
1552
+ status_code=409,
1553
+ )
1554
+ entry_id, flow, _options = found
1555
+ await abort_options_flow_quietly(server.client, flow)
1556
+ schedule_deferred_entry_reload(server.client, entry_id)
1557
+ return JSONResponse(
1558
+ {"success": True, "message": "In-process server reload scheduled"}
1559
+ )
1560
+
1505
1561
  # The sidecar process (server is None) has no Supervisor context
1506
1562
  # and no live server settings — refuse cleanly. The HTTP modes
1507
1563
  # that do pass a server still go through the SUPERVISOR_TOKEN
@@ -1628,14 +1684,27 @@ def build_settings_handlers(
1628
1684
  # either channel; standalone Docker / uvx falls back to the
1629
1685
  # installed package's metadata.
1630
1686
  try:
1631
- version = get_version()
1687
+ # Executor: get_version's distribution-ownership scan reads
1688
+ # metadata for every installed package — too heavy for the
1689
+ # event loop on an endpoint the restart cycle polls.
1690
+ version = await asyncio.to_thread(get_version)
1632
1691
  except Exception: # pragma: no cover — defensive only
1633
1692
  logger.warning("get_version() raised; omitting version from info")
1634
1693
  version = None
1694
+ # ``deployment_mode`` reuses the bug-report detector so the UI and
1695
+ # ha_report_issue can never disagree about where the server runs —
1696
+ # the embedded (in-process custom component) server would otherwise
1697
+ # read as plain "docker" here (/.dockerenv exists in the HA core
1698
+ # container).
1699
+ from ..tools.tools_bug_report import _detect_installation_method
1700
+
1635
1701
  return JSONResponse(
1636
1702
  {
1637
1703
  "is_addon": addon,
1638
1704
  "is_sidecar": is_sidecar,
1705
+ "deployment_mode": (
1706
+ "sidecar" if is_sidecar else _detect_installation_method()
1707
+ ),
1639
1708
  "instance_id": _PROCESS_INSTANCE_ID,
1640
1709
  "started_at": _PROCESS_STARTED_AT,
1641
1710
  "version": version,
@@ -161,7 +161,7 @@
161
161
  <button class="tab" data-panel="entity-visibility" role="tab" id="tab-entity-visibility" aria-controls="panel-entity-visibility" aria-selected="false" tabindex="-1">Entity Visibility</button>
162
162
  <button class="tab" data-panel="accessibility" role="tab" id="tab-accessibility" aria-controls="panel-accessibility" aria-selected="false" tabindex="-1">Accessibility</button>
163
163
  </div>
164
- <div class="restart-notice" id="restartNotice">
164
+ <div class="restart-notice" id="restartNotice" role="status" aria-live="polite">
165
165
  <span class="restart-notice-text" id="restartNoticeText">
166
166
  ⚠ Changes saved. Restart HA-MCP for them to take effect. On next
167
167
  startup, HA-MCP fully removes disabled tools from the MCP tool list.
@@ -265,6 +265,12 @@
265
265
  title="Permanently disables the settings UI: stops this server AND writes ~/.ha-mcp/settings_ui_disabled so it does not respawn on future ha-mcp launches. Delete that file to re-enable."
266
266
  >Permanently disable settings server</button>
267
267
  </div>
268
+
269
+ <!-- Developer section sits at the ABSOLUTE bottom of the panel by
270
+ design (issue #1775): the dev-mode toggle registers hidden
271
+ ha_dev_* tools and should not be obvious to casual users. -->
272
+ <h2 class="adv-section-title beta-section-title">Developer</h2>
273
+ <div id="advDeveloper" class="adv-section"></div>
268
274
  </div>
269
275
  <div class="panel" id="panel-backups" role="tabpanel" aria-labelledby="tab-backups" tabindex="0">
270
276
  <div class="backup-state" id="backupState">Loading backup state…</div>
@@ -217,6 +217,22 @@ async function applyInfoChrome() {
217
217
  'Desktop. Restarting the App (add-on) alone does not refresh your ' +
218
218
  'client\'s cached tool list.';
219
219
  }
220
+ } else if (info.deployment_mode === 'embedded') {
221
+ // In-process (custom component) server: the restart endpoint reloads
222
+ // the server config entry, which reinstalls-if-newer and swaps the
223
+ // worker onto the freshly installed code.
224
+ const rbtn = document.getElementById('restartBtn');
225
+ rbtn.style.display = '';
226
+ rbtn.textContent = 'Restart HA-MCP Server';
227
+ if (noticeEl) {
228
+ noticeEl.textContent =
229
+ '⚠ Changes saved. Click "Restart HA-MCP Server" (reloads the ' +
230
+ 'in-process server integration) for them to take effect. ' +
231
+ 'Disabled tools will be fully removed from the MCP tool list on ' +
232
+ 'next startup. Then refresh the tool list in your AI client — ' +
233
+ 'e.g. refresh tool list on claude.ai, re-add or refresh the ' +
234
+ 'connector in ChatGPT, or close and reopen Claude Desktop.';
235
+ }
220
236
  } else if (info.is_sidecar) {
221
237
  if (noticeEl) {
222
238
  noticeEl.textContent =
@@ -414,7 +430,7 @@ async function _runRestartReloadCycle(previousInstanceId) {
414
430
  async function restartAddon() {
415
431
  if (restartInProgress) return;
416
432
  const btn = document.getElementById('restartBtn');
417
- if (!confirm('Restart the App (add-on) now? The page will reload automatically once the App (add-on) is back online.')) return;
433
+ if (!confirm('Restart HA-MCP now? The page will reload automatically once it is back online.')) return;
418
434
  restartInProgress = true;
419
435
  btn.disabled = true;
420
436
  btn.textContent = 'Restarting…';
@@ -2946,6 +2962,7 @@ const ADVANCED_FIELD_META = {
2946
2962
  code_mode_max_invocations: { label: "Code-mode max invocations", help: "API/tool-call cap per sandbox run. Restart required." },
2947
2963
  code_mode_saved_tools_path:{ label: "Saved-tools path", help: "JSON file where ha_manage_custom_tool persists saved tools across restarts. Restart required." },
2948
2964
  sidecar_pin_port: { label: "Settings UI sidecar port", help: "0 = a new free port each restart (default); set 1024–65535 to pin a fixed port so the settings URL stays stable across restarts. Falls back to a free port if the pinned one is busy. Restart required." },
2965
+ enable_dev_mode: { label: "Developer mode", help: "⚠ DANGER: registers hidden developer tools (ha_dev_manage_server, ha_dev_manage_settings) that let AI agents change server settings and replace the running server version (e.g. install a PR build). For development and testing only. Restart required." },
2949
2966
  };
2950
2967
 
2951
2968
  // Fields that require an MCP-host restart to take effect when changed
@@ -2974,6 +2991,9 @@ const ADVANCED_RESTART_REQUIRED = new Set([
2974
2991
  // The sidecar binds its port once at spawn (run_main), so changing the
2975
2992
  // pin needs a restart to respawn the sidecar on the new port.
2976
2993
  "sidecar_pin_port",
2994
+ // Dev-mode tools register at startup; toggling needs a restart to
2995
+ // (un)register them.
2996
+ "enable_dev_mode",
2977
2997
  ]);
2978
2998
 
2979
2999
  let _advancedFields = [];
@@ -3039,6 +3059,7 @@ async function loadAdvancedSettings() {
3039
3059
  renderAdvancedSection('advToolsSurface', bySection.tools_surface || []);
3040
3060
  renderAdvancedSection('advDiagnostics', bySection.diagnostics || []);
3041
3061
  renderAdvancedSection('advSidecar', bySection.sidecar || []);
3062
+ renderAdvancedSection('advDeveloper', bySection.developer || []);
3042
3063
  applySidecarAvailability(data.is_stdio !== false);
3043
3064
  // Re-render feature flags so the code_mode sub-numerics show up
3044
3065
  // beneath enable_code_mode (race: loadFeatureFlags may have run
@@ -3138,6 +3159,19 @@ function renderAdvancedSection(containerId, fields) {
3138
3159
  const fname = input.dataset.advField;
3139
3160
  const f = _advancedFields.find(x => x.field === fname);
3140
3161
  if (!f) return;
3162
+ // Dev mode arms tools that can rewrite server settings and swap
3163
+ // the running server version — confirm before enabling, matching
3164
+ // the stopSidecar / restart danger-action convention.
3165
+ if (fname === 'enable_dev_mode' && input.checked && !confirm(
3166
+ '⚠ Enable developer mode?\n\n'
3167
+ + 'After the next restart, hidden developer tools are exposed to '
3168
+ + 'connected AI agents. They can change server settings and '
3169
+ + 'replace the running server version. Only enable this for '
3170
+ + 'development and testing.'
3171
+ )) {
3172
+ input.checked = false;
3173
+ return;
3174
+ }
3141
3175
  let v;
3142
3176
  if (input.type === 'checkbox') v = input.checked;
3143
3177
  else if (input.type === 'number') v = (f.type === 'float') ? parseFloat(input.value) : parseInt(input.value, 10);
@@ -5,11 +5,14 @@ This module provides a tool to collect diagnostic information and guide users
5
5
  on how to create effective bug reports.
6
6
  """
7
7
 
8
+ import asyncio
9
+ import importlib
8
10
  import logging
9
11
  import os
10
12
  import platform
11
13
  import re
12
14
  import sys
15
+ import time
13
16
  from pathlib import Path
14
17
  from typing import Annotated, Any
15
18
  from urllib.parse import quote_plus
@@ -21,7 +24,7 @@ from pydantic import Field
21
24
 
22
25
  from ha_mcp import __version__
23
26
 
24
- from .._version import is_running_in_addon
27
+ from .._version import get_version, is_embedded, is_running_in_addon
25
28
  from ..client.supervisor_client import make_supervisor_httpx_client
26
29
  from ..config import Settings, get_global_settings
27
30
  from ..utils.usage_logger import (
@@ -72,21 +75,28 @@ def _detect_installation_method() -> str:
72
75
  """
73
76
  Detect how ha-mcp was installed.
74
77
 
75
- Returns one of: pyinstaller, addon, docker, git, pypi, unknown
78
+ Returns one of: pyinstaller, embedded, addon, docker, git, pypi, unknown
76
79
  """
77
80
  # 1. PyInstaller binary
78
81
  if getattr(sys, "frozen", False):
79
82
  return "pyinstaller"
80
83
 
81
- # 2. Home Assistant Add-on (has supervisor token)
84
+ # 2. In-process server inside HA core (the ha_mcp_tools custom
85
+ # component's "server" entry). Checked BEFORE the docker probe: the
86
+ # HA core container carries /.dockerenv, so without this branch
87
+ # embedded installs misreport as plain docker.
88
+ if is_embedded():
89
+ return "embedded"
90
+
91
+ # 3. Home Assistant Add-on (has supervisor token)
82
92
  if is_running_in_addon():
83
93
  return "addon"
84
94
 
85
- # 3. Docker container (non-addon)
95
+ # 4. Docker container (non-addon)
86
96
  if Path("/.dockerenv").exists():
87
97
  return "docker"
88
98
 
89
- # 4. Git clone - check for .git directory relative to package
99
+ # 5. Git clone - check for .git directory relative to package
90
100
  try:
91
101
  # Go up from tools_bug_report.py -> tools -> ha_mcp -> src -> project_root
92
102
  project_root = Path(__file__).parent.parent.parent.parent
@@ -97,7 +107,7 @@ def _detect_installation_method() -> str:
97
107
  # fall through to the next detection heuristic.
98
108
  pass
99
109
 
100
- # 5. PyPI install - marker file exists in package
110
+ # 6. PyPI install - marker file exists in package
101
111
  try:
102
112
  marker_path = Path(__file__).parent.parent / "_pypi_marker"
103
113
  if marker_path.exists():
@@ -107,10 +117,62 @@ def _detect_installation_method() -> str:
107
117
  # fall through to the default "unknown" result.
108
118
  pass
109
119
 
110
- # 6. Default - unknown
120
+ # 7. Default - unknown
111
121
  return "unknown"
112
122
 
113
123
 
124
+ def _detect_installed_version() -> str | None:
125
+ """Return the ha-mcp version installed ON DISK right now.
126
+
127
+ ``__version__`` is frozen when the process first imports ha_mcp, so after
128
+ an in-place update the running process keeps reporting the old version
129
+ while the disk already carries the new one. Reporting both (plus
130
+ ``version_mismatch``) turns a "which server am I talking to" hunt into a
131
+ single tool call.
132
+ """
133
+ try:
134
+ importlib.invalidate_caches()
135
+ return get_version()
136
+ except Exception as e:
137
+ logger.info("Installed-version probe failed: %s", e)
138
+ return None
139
+
140
+
141
+ def _instance_identity() -> dict[str, Any]:
142
+ """Return process identity (id, start time, uptime).
143
+
144
+ Mirrors the ``/api/settings/info`` fields so a report and the web UI can
145
+ be matched to the same (or different) server process.
146
+ """
147
+ from ..settings_ui import _PROCESS_INSTANCE_ID, _PROCESS_STARTED_AT
148
+
149
+ return {
150
+ "instance_id": _PROCESS_INSTANCE_ID,
151
+ "started_at": _PROCESS_STARTED_AT,
152
+ "uptime_seconds": round(time.time() - _PROCESS_STARTED_AT, 1),
153
+ }
154
+
155
+
156
+ def _format_version_value(diagnostic_info: dict[str, Any]) -> str:
157
+ """Render the version for report surfaces, flagging a stale worker.
158
+
159
+ A failed installed-version probe must stay distinguishable from
160
+ "checked, versions match" — otherwise the exact stale-worker
161
+ condition this field exists to expose disappears whenever the probe
162
+ itself hiccups.
163
+ """
164
+ running = diagnostic_info.get("ha_mcp_version", "Unknown")
165
+ installed = diagnostic_info.get("installed_version")
166
+ if diagnostic_info.get("version_mismatch"):
167
+ return (
168
+ f"{running} (running) — {installed} is installed; "
169
+ "restart to finish applying the update"
170
+ )
171
+ if installed is None:
172
+ return f"{running} (installed-on-disk version could not be verified)"
173
+ return str(running)
174
+
175
+
114
176
  def _detect_platform() -> dict[str, str]:
115
177
  """Detect platform information."""
116
178
  return {
@@ -473,7 +535,8 @@ def _build_formatted_report(
473
535
  report_lines = [
474
536
  "=== ha-mcp Bug Report Info ===",
475
537
  "",
476
- f"ha-mcp Version: {diagnostic_info['ha_mcp_version']}",
538
+ f"ha-mcp Version: {_format_version_value(diagnostic_info)}",
539
+ f"Custom Component: {diagnostic_info.get('component_version') or 'not detected (not installed, or probe failed)'}",
477
540
  f"Installation Method: {diagnostic_info['installation_method']}",
478
541
  f"MCP Transport: {mcp_transport}",
479
542
  f"MCP Client: {_format_client_info_for_template(client_info)}",
@@ -518,6 +581,27 @@ class BugReportTools:
518
581
  def __init__(self, client: Any) -> None:
519
582
  self._client = client
520
583
 
584
+ async def _detect_component_version(self) -> str | None:
585
+ """Read the ha_mcp_tools custom component's version, best-effort.
586
+
587
+ Uses the component's ``get_caller_token`` bootstrap service, whose
588
+ response carries the manifest version (the same field the filesystem
589
+ tools' version gate reads). Returns None when the component is not
590
+ installed or the call fails — the report path must never break on it.
591
+ """
592
+ try:
593
+ resp = await self._client.call_service(
594
+ "ha_mcp_tools", "get_caller_token", {}, return_response=True
595
+ )
596
+ payload = (
597
+ resp.get("service_response", resp) if isinstance(resp, dict) else {}
598
+ )
599
+ version = payload.get("version") if isinstance(payload, dict) else None
600
+ return str(version) if version else None
601
+ except Exception as e:
602
+ logger.info("Component version probe failed: %s", e)
603
+ return None
604
+
521
605
  @tool(
522
606
  name="ha_report_issue",
523
607
  tags={"Utilities"},
@@ -592,9 +676,17 @@ class BugReportTools:
592
676
  config_toggles = _get_config_toggles()
593
677
  mcp_transport = _detect_mcp_transport()
594
678
  client_info = _extract_client_info(ctx)
679
+ installed_version = await asyncio.to_thread(_detect_installed_version)
680
+ component_version = await self._detect_component_version()
595
681
 
596
682
  diagnostic_info: dict[str, Any] = {
597
683
  "ha_mcp_version": __version__,
684
+ "installed_version": installed_version,
685
+ "version_mismatch": bool(
686
+ installed_version and installed_version != __version__
687
+ ),
688
+ "component_version": component_version,
689
+ "instance": _instance_identity(),
598
690
  "installation_method": install_method,
599
691
  "platform": platform_info,
600
692
  "mcp_transport": mcp_transport,
@@ -1093,7 +1185,8 @@ ha_call_service(domain="light", service="turn_on", entity_id="light.example")
1093
1185
 
1094
1186
  ## 🔧 Environment
1095
1187
 
1096
- - **ha-mcp Version:** {diagnostic_info.get("ha_mcp_version", "Unknown")}
1188
+ - **ha-mcp Version:** {_format_version_value(diagnostic_info)}
1189
+ - **Custom Component:** {diagnostic_info.get("component_version") or "not detected (not installed, or probe failed)"}
1097
1190
  - **Installation Method:** {diagnostic_info.get("installation_method", "Unknown")}
1098
1191
  - **MCP Transport:** {mcp_transport} _(auto-detected — correct if wrong)_
1099
1192
  - **MCP Client:** {_format_client_info_for_template(client_info)} _(auto-detected from the MCP `initialize` handshake)_
@@ -1257,7 +1350,8 @@ def _generate_agent_behavior_template(
1257
1350
 
1258
1351
  ## 📊 Environment
1259
1352
 
1260
- - **ha-mcp Version:** {diagnostic_info.get("ha_mcp_version", "Unknown")}
1353
+ - **ha-mcp Version:** {_format_version_value(diagnostic_info)}
1354
+ - **Custom Component:** {diagnostic_info.get("component_version") or "not detected (not installed, or probe failed)"}
1261
1355
  - **Installation Method:** {diagnostic_info.get("installation_method", "Unknown")}
1262
1356
  - **MCP Transport:** {mcp_transport} _(auto-detected — correct if wrong)_
1263
1357
  - **MCP Client:** {_format_client_info_for_template(client_info)} _(auto-detected from the MCP `initialize` handshake)_