ha-mcp-dev 7.5.0.dev543__tar.gz → 7.5.0.dev545__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 (111) hide show
  1. {ha_mcp_dev-7.5.0.dev543/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev545}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/client/rest_client.py +97 -14
  4. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/LICENSE +0 -0
  6. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/README.md +0 -0
  8. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/_version.py +0 -0
  13. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/auth/__init__.py +0 -0
  14. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/auth/consent_form.py +0 -0
  15. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/auth/provider.py +0 -0
  16. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/client/__init__.py +0 -0
  17. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/client/supervisor_client.py +0 -0
  18. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/client/websocket_client.py +0 -0
  19. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/client/websocket_listener.py +0 -0
  20. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/config.py +0 -0
  21. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/errors.py +0 -0
  22. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/py.typed +0 -0
  23. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  24. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  25. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  26. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  27. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  28. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  29. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  30. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  31. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  32. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  33. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  34. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  35. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  36. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  37. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  38. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  39. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  40. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  41. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  42. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  43. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/server.py +0 -0
  44. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/settings_ui.py +0 -0
  45. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/smoke_test.py +0 -0
  46. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/__init__.py +0 -0
  47. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/backup.py +0 -0
  48. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  49. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/device_control.py +0 -0
  50. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/enhanced.py +0 -0
  51. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/helpers.py +0 -0
  52. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/reference_validator.py +0 -0
  53. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/registry.py +0 -0
  54. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/smart_search.py +0 -0
  55. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_addons.py +0 -0
  56. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_areas.py +0 -0
  57. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  58. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  59. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_calendar.py +0 -0
  60. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_camera.py +0 -0
  61. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_categories.py +0 -0
  62. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_code.py +0 -0
  63. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  64. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  65. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  66. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  67. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  68. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  69. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_energy.py +0 -0
  70. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_entities.py +0 -0
  71. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  72. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_groups.py +0 -0
  73. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_hacs.py +0 -0
  74. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_history.py +0 -0
  75. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_integrations.py +0 -0
  76. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_labels.py +0 -0
  77. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  78. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_registry.py +0 -0
  79. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_resources.py +0 -0
  80. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_search.py +0 -0
  81. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_service.py +0 -0
  82. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_services.py +0 -0
  83. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_system.py +0 -0
  84. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_todo.py +0 -0
  85. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_traces.py +0 -0
  86. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_updates.py +0 -0
  87. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_utility.py +0 -0
  88. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  89. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  90. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/tools_zones.py +0 -0
  91. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/tools/util_helpers.py +0 -0
  92. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/transforms/__init__.py +0 -0
  93. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/transforms/categorized_search.py +0 -0
  94. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  95. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/__init__.py +0 -0
  96. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/config_hash.py +0 -0
  97. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/data_paths.py +0 -0
  98. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/domain_handlers.py +0 -0
  99. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  100. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  101. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/operation_manager.py +0 -0
  102. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/python_sandbox.py +0 -0
  103. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp/utils/usage_logger.py +0 -0
  104. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  105. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  106. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  107. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  108. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  109. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/tests/__init__.py +0 -0
  110. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/tests/test_constants.py +0 -0
  111. {ha_mcp_dev-7.5.0.dev543 → ha_mcp_dev-7.5.0.dev545}/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.5.0.dev543
3
+ Version: 7.5.0.dev545
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.5.0.dev543"
7
+ version = "7.5.0.dev545"
8
8
  description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -136,6 +136,13 @@ class HomeAssistantClient:
136
136
  verify=self.verify_ssl,
137
137
  )
138
138
 
139
+ # Lazy-populated by ``_is_supervised_install``. ``None`` means
140
+ # "not probed yet"; ``True`` is cached for the session lifetime
141
+ # (HAOS-ness can't change at runtime). ``False`` is NOT cached so a
142
+ # transient probe failure on the first call doesn't permanently
143
+ # disable the supervised branch — subsequent calls re-probe.
144
+ self._supervised_detected: bool | None = None
145
+
139
146
  logger.info(f"Initialized Home Assistant client for {self.base_url}")
140
147
 
141
148
  async def __aenter__(self) -> "HomeAssistantClient":
@@ -455,17 +462,30 @@ class HomeAssistantClient:
455
462
  async def get_error_log(self) -> str:
456
463
  """Get Home Assistant error log.
457
464
 
458
- Branch on ``is_running_in_addon()``: inside the add-on container,
459
- HA Core's ``bootstrap.py`` sets ``err_log_path = None`` when the
460
- ``SUPERVISOR`` env var is present, so ``hass.data[DATA_LOGGING]``
461
- is never populated and the ``APIErrorLog`` view is not registered
462
- ``/api/error_log`` returns 404 by-design on HA OS / Supervised.
463
- Route to ``_supervisor_logs_get("core")`` on this branch: same
464
- content (HA Core's container log) via a different transport
465
- (Supervisor REST). On non-addon installs keep the
466
- ``/api/error_log`` proxy path.
467
-
468
- Same root cause and fix shape as ``get_addon_logs`` — see #1116.
465
+ Three-way branch depending on how this client reaches HA:
466
+
467
+ - **Addon context** (``is_running_in_addon()`` True i.e.
468
+ ``SUPERVISOR_TOKEN`` is set, meaning this process is the
469
+ ha-mcp add-on container talking to the Supervisor sibling):
470
+ go direct to Supervisor REST at
471
+ ``http://supervisor/core/logs``.
472
+ - **External client HAOS/Supervised** (``is_running_in_addon()``
473
+ False AND ``hassio`` is listed in HA's loaded components): use
474
+ the HA Core hassio proxy at ``/api/hassio/core/logs`` with the
475
+ user LLA. ``/api/error_log`` is unregistered by design on
476
+ Supervised installs — HA Core's ``bootstrap.py:646-671`` sets
477
+ ``err_log_path = None`` when ``SUPERVISOR`` is in the env, so
478
+ ``hass.data[DATA_LOGGING]`` is never populated and the
479
+ ``APIErrorLog`` view (``api/__init__.py:89-90``) never registers.
480
+ The hassio proxy reaches the same underlying log stream.
481
+ - **External client → Container/pip HA** (neither of the above):
482
+ keep the historical ``/api/error_log`` proxy path.
483
+
484
+ The middle branch was discovered by the HAOS E2E tier (#1326): the
485
+ test harness runs ha-mcp externally against a booted HAOS, hits
486
+ the unregistered endpoint, and the old binary branch surfaced as
487
+ a confusing 404. Verified end-to-end with the user's real HAOS
488
+ and against HA Core source.
469
489
 
470
490
  Raises:
471
491
  HomeAssistantAuthError: 401, or empty ``SUPERVISOR_TOKEN`` on
@@ -479,9 +499,72 @@ class HomeAssistantClient:
479
499
  logger.debug("Fetching error log via Supervisor direct (core service)")
480
500
  return await self._supervisor_logs_get("core")
481
501
 
482
- logger.debug("Fetching error log via HA Core proxy")
483
- response = await self._request("GET", "/error_log")
484
- return response if isinstance(response, str) else str(response)
502
+ if await self._is_supervised_install():
503
+ logger.debug(
504
+ "Fetching error log via HA Core /hassio/core/logs proxy (supervised)"
505
+ )
506
+ raw_response = await self._raw_request(
507
+ "GET",
508
+ "/hassio/core/logs?lines=20000",
509
+ headers={"Accept": "text/plain"},
510
+ )
511
+ return raw_response.text
512
+
513
+ logger.debug("Fetching error log via HA Core proxy (Container/pip)")
514
+ raw_response = await self._raw_request(
515
+ "GET", "/error_log", headers={"Accept": "text/plain"}
516
+ )
517
+ return raw_response.text
518
+
519
+ async def _is_supervised_install(self) -> bool:
520
+ """Detect whether the target HA is a Supervised / HAOS install.
521
+
522
+ Probes ``/api/config`` once per client instance and returns
523
+ ``"hassio" in components``. Cached for the session lifetime on
524
+ BOTH definite outcomes (True or False), since a successful
525
+ ``/api/config`` response with or without ``hassio`` is a
526
+ definitive signal — HA's loaded-components set doesn't change
527
+ between Supervised and Container at runtime.
528
+
529
+ Cache is NOT poisoned on probe failure: a transient network
530
+ glitch or HTTP error returns False without setting the cache,
531
+ so the next call re-probes. This is the only path that
532
+ intentionally fails open — caller proceeds on the historical
533
+ Container branch (which on HAOS will also fail, but with a
534
+ clearer 404 than a probe-side exception would surface).
535
+
536
+ Auth / connection / HTTP errors are caught explicitly; runtime
537
+ bugs (TypeError, AttributeError) and BaseException derivatives
538
+ (KeyboardInterrupt, CancelledError) deliberately propagate so
539
+ they're not silenced as "not supervised".
540
+ """
541
+ if self._supervised_detected is not None:
542
+ return self._supervised_detected
543
+ try:
544
+ config = await self._request("GET", "/config")
545
+ except (
546
+ HomeAssistantAuthError,
547
+ HomeAssistantAPIError,
548
+ HomeAssistantConnectionError,
549
+ httpx.HTTPError,
550
+ TimeoutError,
551
+ ) as exc:
552
+ # Fail-open on transport / HTTP-layer failures only. Note:
553
+ # a 401 here likely means the LLA is bad and the /error_log
554
+ # fallback will also 401 — the user gets a clearer auth error
555
+ # from that path than a swallowed probe error would surface.
556
+ # Logged at WARNING so it's visible at default log levels
557
+ # without spamming on every call (probe runs at most once
558
+ # per session per outcome).
559
+ logger.warning(
560
+ "Supervised-install probe failed (fail-open to non-supervised): %r",
561
+ exc,
562
+ )
563
+ return False
564
+ components = config.get("components", []) if isinstance(config, dict) else []
565
+ is_supervised = isinstance(components, list) and "hassio" in components
566
+ self._supervised_detected = is_supervised
567
+ return is_supervised
485
568
 
486
569
  async def get_addon_logs(self, slug: str) -> str:
487
570
  """Fetch an add-on's container logs.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.5.0.dev543
3
+ Version: 7.5.0.dev545
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