Skip to content

fix: route addon and HA core log fetches directly to Supervisor on addon installs - #1126

Merged
Patch76 merged 5 commits into
homeassistant-ai:masterfrom
Patch76:fix/issue-1116-supervisor-token-direct-endpoint
May 6, 2026
Merged

fix: route addon and HA core log fetches directly to Supervisor on addon installs#1126
Patch76 merged 5 commits into
homeassistant-ai:masterfrom
Patch76:fix/issue-1116-supervisor-token-direct-endpoint

Conversation

@Patch76

@Patch76 Patch76 commented May 5, 2026

Copy link
Copy Markdown
Member

What does this PR do?

Closes #1116. On add-on installs, ha_get_logs(source="supervisor", slug=...) returns 403 for every slug because HA Core rejects the SUPERVISOR_TOKEN against the proxy route /api/hassio/addons/{slug}/logs. Route around HA Core entirely on add-on installs by hitting the documented Supervisor REST API at http://supervisor/addons/{slug}/logs directly with the Supervisor token — same access pattern tools_bug_report.py:_fetch_addon_logs already uses for self-logs.

The HA-Core-proxy path stays as the fallback for non-addon installs (Docker, pyinstaller, pip pointing at a normal HA URL with admin LLA), where it continues to work fine. The bug only manifests on the addon-container call against http://supervisor/core.

Branching gate is the existing is_running_in_addon() helper in src/ha_mcp/_version.py — no fourth duplicate of the SUPERVISOR_TOKEN env-var check.

Companion config change — hassio_role: default → manager

KP13 verified locally that the Supervisor token does not grant /addons/{slug}/logs (or /{service}/logs) access on the default role. Bumped to manager in both homeassistant-addon/config.yaml and homeassistant-addon-dev/config.yaml, with an inline comment citing the live test.

Scope addition — source="system_service"

KP13's PR-comment 12:42 surfaced that #1116 also covers the missing-feature case: HA-Supervisor-managed system service logs (/supervisor/logs, /host/logs, /core/logs, /dns/logs, /audio/logs, /multicast/logs, /observer/logs) were not reachable via any source value. Added a new source="system_service" value matching KP13's data-type-named source convention (#911 / #1003).

  • New slug semantics for source="system_service": a service name from SYSTEM_SERVICE_SLUGS = {supervisor, host, core, dns, audio, multicast, observer}. Caller-layer enum validation gives users the allowed set before the request fires.
  • Slug namespace overlap is acceptable because the source parameter disambiguates: slug="supervisor" under source="system_service" means the Supervisor service's own logs, not an add-on with that name. Docstring lists both slug semantics separately.
  • New REST-client method _get_system_service_logs(service) extracted to a shared _supervisor_logs_get(path) helper alongside _get_addon_logs_via_supervisor; both branches now share error handling, JSON-envelope parsing, role-hint 403 messaging, and the fail-fast empty-token guard.

KP13 CR disposition (16 of 17 adopted)

# Item Disposition
1 Empty SUPERVISOR_TOKEN silent blank Bearer ✅ Fail-fast HomeAssistantAuthError with distinct "absent at call time" message
2 403 not mapped distinctly ✅ Carved out before generic >=400 with role-hint suggestion
3 Stale _get_supervisor_log docstring ✅ Boy-Scout: dropped #950-only framing, describes branch-on-is_running_in_addon()
4 Misleading "mirrors _fetch_addon_logs" framing ✅ Rewritten to enumerate scope (arbitrary slug vs self) + role (manager vs default) differences
5 verify_ssl no-op for http://supervisor ✅ Kept with explicit symmetry comment citing #1128's 3-site convention (KP13's option B)
6 Error envelope JSON not parsed json.loads(text_body).get("message") first, then text/reason_phrase fallback
7 Catch-order timeout vs HTTPError no observable diff ✅ Distinct "Timeout..." / "Transport error..." messages
8 level param applies to source="supervisor" 📝 Already emitted at tools_utility.py:139supervisor is in the level-not-applicable warning tuple, pinned by test_level_param_emits_warning_for_supervisor_source. PR adds system_service to the same tuple + parallel test for parity
9 No logger.warning on >=400 raise path ✅ Logged with status + path before every 4xx raise
10 Missing/empty SUPERVISOR_TOKEN test test_raises_auth_error_on_empty_supervisor_token + parallel for system_service
11 Generic httpx.HTTPError untested test_raises_connection_error_on_remote_protocol_error
12 Tier-3 reason-phrase fallback parity test_empty_body_no_reason_phrase_uses_placeholder
13 ctor-kwargs propagation untested verify + timeout asserted on the URL+auth happy-path test
14 test_addon_install_uses_supervisor_direct mock-the-mock ✅ Shrunk to gate-only check; URL/auth contract delegated to dedicated class
15 Patch path brittleness ✅ Migrated to patch("httpx.AsyncClient", ...) — robust to either import httpx or from httpx import AsyncClient
16 Inaccurate gate description ✅ Docstring says is_running_in_addon(), not "SUPERVISOR_TOKEN env present"
17 Drop redundant narration ✅ Per-test/fixture/class-level docstrings trimmed

Test coverage

File Class What it pins
tests/src/unit/test_tools_utility_supervisor_logs.py TestGetAddonLogsViaSupervisor (extended) URL+auth+ctor-kwargs, 401/403/404/JSON-envelope/empty-body+reason/empty-body-no-reason, timeout vs transport distinct messages, RemoteProtocolError, empty-token fail-fast
TestGetSystemServiceLogs (new) Service URL+auth+ctor-kwargs, empty-token fail-fast, 403 role-hint, 404
TestGetSystemServiceLogWrapper (new) Response shape, missing-slug validation, invalid-slug enum hint, all-7-services dispatch (parametrized), 403 role-hint suggestion, level-warning parity
TestGetAddonLogsBranchSelection (modified) Gate-only check after #14 shrink

Local: 1703 unit tests pass, ruff clean, mypy clean.

Out of scope

The issue body asks for an integration/contract test against a real Supervisor-equipped HA. The existing E2E suite uses a Supervisor-less homeassistant/home-assistant testcontainer; spinning up a Supervisor side-car is outside this PR. Filing as a Suggested Improvement.

Round 3 — error_log + slug warning + role docs (commit a49dbc3)

KP13's round-2 review surfaced three more items addressed in a49dbc3:

  • Critical 1: source="error_log" is broken on Supervisor installs — same root cause as [BUG] ha_get_logs(source="supervisor") returns 403 for every slug on add-on installs #1116. HA Core's bootstrap.py:641-651 sets err_log_path = None when the SUPERVISOR env var is present, so hass.data[DATA_LOGGING] is never populated and the APIErrorLog view is not registered (/api/error_log → 404 by-design). Fix shape mirrors get_addon_logs: branch HomeAssistantClient.get_error_log() on is_running_in_addon()_supervisor_logs_get("core") for the addon branch (HA Core's container log via Supervisor — same content, different transport). Pinned by TestGetErrorLogBranchSelection.
  • Nit 2: slug parameter incompatibility warning — third warning in tools_utility.ha_get_logs, parallels the existing level and entity_id/end_time warnings. Pinned by TestSlugParameterIncompatibilityWarning parametrized over four non-supervisor sources (warn) and two supervisor sources (no warn).
  • Nit 3: Role escalation undocumented### Supervisor Permissions section in homeassistant-addon/DOCS.md, plus ### Permissions subsection in homeassistant-addon-dev/DOCS.md, calling out the hassio_role: manager requirement.

Local: 1711 unit tests pass, ruff clean.

Type of change

  • 🐛 Bug fix
  • ✨ New feature (system_service source)
  • 📚 Documentation
  • 🔧 Maintenance/refactor
  • 🧪 Tests only
  • 💥 Breaking change

Testing

  • I have tested these changes with a LLM agent (live bug reproduction on stable v7.4.1; post-merge dev-channel deploy will confirm fix)
  • All automated tests pass (uv run pytest)
  • Code follows style guidelines (uv run ruff check)

Checklist

  • I have updated documentation if needed (no docs touched; bug fix in client layer + new source param documented in tool docstring)

Closes #1116

Addresses homeassistant-ai#1116. The HA Core proxy at /api/hassio/addons/{slug}/logs
returns 403 against SUPERVISOR_TOKEN on current HA Core releases. Route
around HA Core entirely on add-on installs by hitting the Supervisor
REST API at http://supervisor/addons/{slug}/logs directly — same
pattern tools_bug_report.py already uses for self-logs.

The HA-Core-proxy path stays as the fallback for non-addon installs
(Docker/pip with admin LLA). Branching gate is the existing
is_running_in_addon() helper in _version.py — no fourth duplicate of
the SUPERVISOR_TOKEN env-var check.

Test coverage pins both branches plus the gate selection — see new
TestGetAddonLogsViaSupervisor and TestGetAddonLogsBranchSelection
classes in test_tools_utility_supervisor_logs.py.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves an issue where fetching add-on logs failed in Supervisor-equipped environments due to HA Core proxy restrictions. By introducing a direct communication path to the Supervisor API when running as an add-on, the fix ensures reliable log retrieval while maintaining backward compatibility for non-add-on installations.

Highlights

  • Direct Supervisor API Access: Implemented a direct connection to the Supervisor REST API for add-on environments, bypassing the HA Core proxy which was rejecting requests with 403 errors.
  • Environment-Aware Routing: Added logic to detect if the application is running within an add-on container, allowing it to dynamically choose between the direct Supervisor path and the existing HA Core proxy fallback.
  • Comprehensive Test Coverage: Added new test suites to verify both the direct Supervisor path and the fallback mechanism, ensuring robust error handling and correct branch selection.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Patch76

Patch76 commented May 5, 2026

Copy link
Copy Markdown
Member Author

Implementation Summary

Choices Made:

  • Direct Supervisor REST API on add-on installs, HA-Core-proxy as fallback for non-addon installs — this is exactly what KP13's issue body suggests. The fix sits in rest_client.py:get_addon_logs and branches on is_running_in_addon().
  • Reuse is_running_in_addon() from _version.py:51-58 as the gate. There were already three SUPERVISOR_TOKEN-detection sites in the codebase (_version.py canonical, settings_ui.py:111 local mirror, tools_bug_report.py:67/179); adding a fourth would have been needless duplication.
  • Standalone httpx.AsyncClient block (new helper _get_addon_logs_via_supervisor) rather than a header-override on _raw_request. Mirrors the pattern tools_bug_report.py:_fetch_addon_logs already uses (tools_bug_report.py:184-188) — both the base URL (http://supervisor) and the token (SUPERVISOR_TOKEN) differ from HomeAssistantClient.httpx_client's configuration, so a fresh client is the cleaner shape.
  • Same exception surface in both branches (HomeAssistantAuthError / HomeAssistantAPIError with status_code / HomeAssistantConnectionError) so the wrapper layer in tools_utility.py:_get_supervisor_log keeps working unchanged regardless of which branch ran.
  • Empty-body fallback to reason_phrase in the Supervisor-direct branch — pre-existing pattern from _raw_request (rest_client.py:159-176), preserved for consistency so a 5xx with empty body doesn't surface as "API error: 502 - " with a blank tail.
  • Test fixture redesign — split into non_addon_install and addon_install fixtures. The existing TestGetAddonLogs class now uses an autouse _force_non_addon fixture to guarantee the HA-Core-proxy path; otherwise CI environments where SUPERVISOR_TOKEN happens to be set could route the existing tests through the new branch and silently bypass the mock_client.httpx_client mock.

Problems Encountered:

  • None during implementation. Issue body had a clear root-cause analysis from KP13, the matching tools_bug_report.py pattern was already in the codebase, and the new tests pinned both branches plus the gate selection. 1682/1682 unit tests pass locally.

Suggested Improvements:

  • Real-Supervisor integration test (per the test-coverage-gap ask in the issue body) — out of scope for this PR. The current E2E suite uses a Supervisor-less homeassistant/home-assistant testcontainer and there is no precedent in tests/src/e2e/conftest.py for spinning up a Supervisor side-car. Adding Supervisor-equipped E2E coverage broadly (not just for the addon-logs path) would benefit from a separate maintainer discussion on testcontainer composition and CI cost. Filed as [FEATURE] Add Supervisor-equipped E2E test infrastructure #1129 for tracking — three open architecture questions there (container choice, CI cost budget, test-suite layout).
  • HA-Core "regression" investigation — there is no regression. Bisected against home-assistant/core: homeassistant/components/hassio/http.py is byte-identical between 2026.4.0 and 2026.4.4, and the PATHS_ADMIN regex has listed addons/[^/]+/logs(/follow|/boots/-?\d+(/follow)?)? since at least 2025.3.0. The auth gate (is_admin = request[KEY_AUTHENTICATED] and request[KEY_HASS_USER].is_admin in http.py:162-166) has been unchanged across that whole span. The 403 is documented behavior, not a recent break: SUPERVISOR_TOKEN (add-on-issued) does not authenticate as the Core-internal HASSIO_USER admin that PATHS_ADMIN requires — the HA-Core-proxy was never the right path for the add-on-container case. The April 2026 hassio-hardening PRs (#169226, #169299, #169324, #169325, #169329, #169340) all merged to dev 3-4 days after the 2026.4.4 tag, and on inspection none of them touch the PATHS_ADMIN regex or the addons/{slug}/logs auth path — they cover services.py, websocket_api.py, addon_panel.py, discovery.py, and const.py respectively; #169299's http.py change touches only the onboarding-bypass branch, which doesn't apply to onboarded users. The "verified working" phrasing on fix: route supervisor add-on log fetches through HA Core REST proxy #951 must therefore reflect either a different code path (e.g. supervisor/logs) or a non-add-on token context at test time. The fix in this PR is unaffected — it routes around a long-standing access mismatch via the semantically correct token-against-endpoint pairing (Supervisor token against Supervisor itself).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a direct communication path with the Supervisor REST API for fetching add-on logs when running within a Home Assistant add-on environment, while maintaining the HA Core proxy as a fallback for other installation types. This change resolves issues where the proxy would reject certain token and path combinations. Feedback was provided to ensure that the newly introduced httpx.AsyncClient consistently applies the verify_ssl configuration used elsewhere in the HomeAssistantClient class.

Comment thread src/ha_mcp/client/rest_client.py
Per Gemini review on PR homeassistant-ai#1126: mirrors the verify=self.verify_ssl
pattern from rest_client.py:125 (class-level httpx_client). The
http://supervisor URL is plain HTTP and TLS-irrelevant in practice,
but the parameter keeps the constructor consistent with the
established HomeAssistantClient shape.

mock_client fixture extended with verify_ssl = True so the existing
Supervisor-direct tests exercise the new attribute access path.
@Patch76

Patch76 commented May 5, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the get_addon_logs method in the REST client to support direct communication with the Supervisor API when running within a Home Assistant add-on environment. This change bypasses the HA Core proxy to avoid authentication issues and includes comprehensive unit tests for both the direct and fallback paths. Feedback was provided regarding the efficiency of instantiating a new httpx.AsyncClient for every request, suggesting the use of a persistent client instead.

Comment thread src/ha_mcp/client/rest_client.py
@Patch76

Patch76 commented May 5, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the get_addon_logs method in the REST client to support direct communication with the Supervisor API when running as a Home Assistant add-on. This change bypasses the HA Core proxy, which previously caused authentication issues in certain environments. The update includes a new internal helper method, _get_addon_logs_via_supervisor, and comprehensive unit tests covering success scenarios, error mapping, and branch selection logic. Feedback was provided to ensure docstring consistency by using an action verb in the internal helper's documentation.

Comment thread src/ha_mcp/client/rest_client.py Outdated
…verb

Per Gemini review on PR homeassistant-ai#1126: aligns the internal helper's docstring
with the action-verb shape of nearby Fetch*/Get* methods on
HomeAssistantClient. The .gemini/styleguide.md MCP-Tool-Docstrings
section technically scopes to public @tool functions, so this is a
consistency-nit rather than a styleguide-mandated fix — but matches the
local pattern.
@Patch76
Patch76 marked this pull request as ready for review May 5, 2026 09:06
@Patch76
Patch76 requested review from a team and julienld May 5, 2026 09:06
@Patch76
Patch76 enabled auto-merge (squash) May 5, 2026 09:08
kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 5, 2026
…only, dev94

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kingpanther13

Copy link
Copy Markdown
Member

When I tested it locally it still isn't working till I bumped the hassio role. So requesting we bump hassio_role: default → manager in both homeassistant-addon/config.yaml and
homeassistant-addon-dev/config.yaml , it'll make this work.

Also I just caught that the tool still doesn't access the actual director supervisor logs (system) like it is intended to do, that was part of the intent of issue #1116 to address that as well
I thought it was the same but but it turns out the tool just doesn't currently have the capability. Will request we add that in ( access to /supervisor/logs, /host/logs, /core/logs, /dns/logs, /audio/logs, /multicast/logs, or /observer/logs. would need a new source (e.g. source="system_service" with a slug enum)) and change hassio role as above. Will do pr review toolkit in a few minutes too with other requested changes, but those are the biggest things just to get this to work in the first place.

@kingpanther13 kingpanther13 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to the above comment, please address:

Critical

  1. Empty SUPERVISOR_TOKEN silently produces blank Bearer. rest_client.py:482 defaults to "". If the gate ever disagrees with reality (detection bug, dev sideload), Supervisor 401s and the user sees "Invalid Supervisor token" — wrong diagnosis. Fail-fast with a config-error message, or fall through to the proxy branch with a logger.warning.
  2. 403 not mapped distinctly. rest_client.py:498-505 lumps the actual #1116 symptom into the generic >= 400 path. Add a 403 branch with a remediation hint (role / addon manifest), since that's the failure mode this PR is most likely to surface during the role rollout.
  3. Stale tool docstring. tools_utility.py:622-628 still describes the HA-Core-proxy path and references #950. Boy-Scout — update to describe the new branch-on-is_running_in_addon() behavior.
  4. Misleading "mirrors _fetch_addon_logs" framing. rest_client.py:475. That helper is hardcoded to slug=self and works on default role; this helper takes arbitrary slugs and (will) need manager. The docstring should call out the scope/role difference instead of claiming mirroring.

Important
5. verify=self.verify_ssl is a no-op for http://supervisor (rest_client.py:481). Drop it, or add a one-liner comment that it's there for symmetry only.
6. Error envelope not parsed. Supervisor returns {"result":"error","message":"..."} JSON on some 4xx paths. Currently the raw JSON string lands in the user-facing message. Try json.loads(text_body).get("message") first, then fall back to text/reason_phrase.
7. Catch-order split with no observable difference. TimeoutException and HTTPError both raise HomeAssistantConnectionError with near-identical messages. Differentiate ("Timeout..." vs "Transport error...") or collapse to one clause.
8. level param still applies to source="supervisor" (tools_utility.py:139). Meaningless for raw container stdout — now that slug paths actually return content, users will pass level="ERROR" and get nonsense filtering. Drop supervisor from the tuple or warn when both level and slug are set.
9. No logger.warning on the >= 400 raise path in _get_addon_logs_via_supervisor. Operators have to enable debug to diagnose. Log status + slug at warning level on the raise side.

Test gaps
10. Missing/empty SUPERVISOR_TOKEN path untested. Pin behavior either way (fail-fast or "blank Bearer → 401").
11. Generic non-timeout httpx.HTTPError (e.g. RemoteProtocolError) untested. The except httpx.HTTPError clause can be silently narrowed by a future refactor.
12. Tier-3 reason-phrase fallback untested — empty body + empty reason_phrase"<empty body>". TestRawRequestEmptyBodyFallback covers this for the proxy branch; parity gap for the supervisor branch.
13. verify_ssl + timeout ctor-kwargs propagation untested. mock_async_client_class exposes client_class but no test asserts on client_class.call_args.kwargs. Cheap to add to the happy-path test — a regression that hard-codes verify=True or drops the timeout would currently keep CI green.
14. TestGetAddonLogsBranchSelection::test_addon_install_uses_supervisor_direct is mock-the-mock. Duplicates the URL+auth assertions in the dedicated class without re-asserting them, so the test would pass for any implementation that calls httpx.AsyncClient(...).get(...). Either tighten it to assert URL + Authorization on this path too, or shrink to a one-liner that only verifies the gate was consulted (and explicitly delegates contract to the dedicated class).
15. Patch path brittleness. patch("ha_mcp.client.rest_client.httpx.AsyncClient", ...) works only because httpx is imported as a module. A future from httpx import AsyncClient silently breaks it. Prefer patch("httpx.AsyncClient", ...).

Comment / docstring trims
16. Inaccurate gate description. rest_client.py:441 says "On add-on installs (SUPERVISOR_TOKEN env present)". The gate is is_running_in_addon(), not the env var (token is read separately at line 481 with a "" default). If is_running_in_addon ever stops keying off that env var the docstring lies.
17. Drop redundant narration:
- _force_non_addon fixture docstring restates the decorator.
- mock_async_client_class docstring narrates the `async with` mocking pattern (implementation detail).
- Per-test docstrings ("The fix's primary contract...", "401 from Supervisor maps to...") restate test names — drop or trim.
- TestGetAddonLogsViaSupervisor class docstring duplicates the _get_addon_logs_via_supervisor production-code #1116 narrative — keep one copy in the production code.

Critical (review items 1-4):
- Empty SUPERVISOR_TOKEN now fail-fast HomeAssistantAuthError with distinct
  message ("absent at call time"), so detection/config mismatches don't
  read as "token rejected" (item 1).
- 403 carved out as distinct branch with role-hint suggestion + warning log
  before raise — most-likely cause for homeassistant-ai#1116-class failures is now hassio_role
  too low (item 2).
- _get_supervisor_log docstring updated to describe the branch-on-
  is_running_in_addon() behavior, drop homeassistant-ai#950-only framing (item 3).
- _get_addon_logs_via_supervisor docstring rewritten to enumerate
  scope/role differences vs _fetch_addon_logs instead of claiming mirror (item 4).

Important (items 5-9):
- verify=self.verify_ssl kept with explicit symmetry comment citing homeassistant-ai#1128's
  three-site convention (item 5, KP13's option B).
- Supervisor's {"result":"error","message":"..."} JSON envelope now parsed
  before the raw-text fallback (item 6).
- Distinct timeout vs transport error messages (item 7).
- logger.warning fires before every 4xx raise so operators see status+path
  without enabling debug (item 9).

Test gaps (items 10-15):
- New: test_raises_auth_error_on_empty_supervisor_token (item 10).
- New: test_raises_connection_error_on_remote_protocol_error (item 11).
- New: test_empty_body_no_reason_phrase_uses_placeholder for tier-3 fallback
  parity with the proxy branch (item 12).
- ctor-kwargs (verify + timeout) asserted on the URL+auth happy path so a
  regression that hard-codes either keeps CI red (item 13).
- TestGetAddonLogsBranchSelection::test_addon_install_uses_supervisor_direct
  shrunk to gate-only — URL/auth contract delegated to dedicated class (item 14).
- patch path migrated from "ha_mcp.client.rest_client.httpx.AsyncClient" to
  "httpx.AsyncClient" — robust to both \`import httpx\` and a future
  \`from httpx import AsyncClient\` (item 15).

Comment trims (items 16-17):
- get_addon_logs docstring gate description corrected to is_running_in_addon()
  rather than "SUPERVISOR_TOKEN env present" (item 16).
- Per-test/fixture/class-level redundant narration trimmed (item 17).

Decline:
- Item 8 (level applies to source="supervisor"): warning is already emitted
  at tools_utility.py:139 — \`supervisor\` is in the level-not-applicable
  warning tuple, pinned by test_level_param_emits_warning_for_supervisor_source.
  This PR adds \`system_service\` to the same tuple plus a parallel test for
  parity.

Scope additions per KP13's PR-comment 12:42:
- hassio_role: default → manager in both addon config.yamls, with comment
  citing the live test result.
- New source="system_service" with slug enum {supervisor, host, core, dns,
  audio, multicast, observer}. Hits http://supervisor/<service>/logs via the
  same direct-Supervisor pattern (extracted to shared _supervisor_logs_get
  helper). Caller-layer slug-enum validation gives users the allowed-set
  before the request fires.
@Patch76

Patch76 commented May 6, 2026

Copy link
Copy Markdown
Member Author

Implementation Summary

Round 2 of CR addressing — full 17-item disposition table is in the PR body (just refreshed).

Choices Made:

  • Shared _supervisor_logs_get(path) helper in rest_client.py so addon-logs and system-service logs share the empty-token fail-fast, 401/403 carving, JSON-envelope parsing, distinct timeout/transport messages, and verify_ssl symmetry comment. Pre-PR the addon path was inline; the new system-service variant would have duplicated all of it. Helper takes the <path> between http://supervisor/ and /logs so addon-logs pass f"addons/{slug}" and system-service pass the bare service name.
  • Fail-fast on empty SUPERVISOR_TOKEN (KP13 review item 1, KP13's option A): mirrors _restart_addon's pattern at settings_ui.py:780-789 for user-visible features. The is_running_in_addon() gate keys off truthy SUPERVISOR_TOKEN, so empty-token-at-call-time signals a detection/config mismatch — distinct error message ("absent at call time") so it doesn't read as "token rejected".
  • verify=self.verify_ssl kept with symmetry comment (item 5, KP13's option B): refactor: pass verify_ssl to remaining direct-Supervisor httpx callers #1128's PR-body explicitly establishes the 3-site convention with the same "no-op on plain HTTP but bot-reviewers will keep flagging asymmetry" rationale. Drop here would have re-broken what refactor: pass verify_ssl to remaining direct-Supervisor httpx callers #1128 just unified.
  • source="system_service" data-type-named, not route-named (PR-comment scope-add): matches KP13's own feat: consolidate ha_get_statistics into ha_get_history via source parameter #911 (source: Literal["history", "statistics"]) and feat: surface integration log levels in ha_get_logs/integration/addon (#956) #1003 (logger source) precedent for data-type-discriminating source enums. Slug namespace overlaps with source="supervisor" are acceptable because the source parameter disambiguates; docstring lists both slug semantics separately.
  • Slug-enum runtime-validated against SYSTEM_SERVICE_SLUGS frozenset (caller layer, not Literal type): the same slug parameter has different semantics for source="supervisor" (free addon-slug string) vs source="system_service" (closed enum). Caller-layer validation gives the LLM the allowed-set in the error suggestion before the request fires; module-level frozenset is the project's empirically dominant pattern (vs nested Literal[...] per source).
  • hassio_role: default → manager in both addon manifests with inline comment citing the live test result. Required for both /addons/<slug>/logs and /<service>/logs Supervisor REST paths.
  • 17-item disposition: 16 adopted + 1 declined. Item 8 (level applies to source="supervisor") declined with rationale: warning is already emitted at tools_utility.py:139supervisor is in the level-not-applicable warn-tuple, pinned by test_level_param_emits_warning_for_supervisor_source. PR adds system_service to the same tuple plus a parity test. If the intent of item 8 was harder behavior (reject the call instead of warn), happy to revisit — but the existing pattern matches all other source/level pairs in the function.

Problems Encountered:

  • Test response-shape parsing: first iteration of TestGetSystemServiceLogWrapper::test_403_role_hint_suggestion looked at body.get("suggestions", []) (top-level), but create_error_response nests them at body["error"]["suggestions"]. Fixed in 4463d7d; surfaced only because the test framework re-runs caught the AssertionError immediately. (One-pytest-iteration cost.)
  • No conflict markers, no merge conflicts at HEAD. Branch hasn't been rebased onto current master since open; happy to rebase before merge if desired.

Suggested Improvements (post-merge):

  • Live integration coverage for the Supervisor-direct paths is filed as out-of-scope here — the existing E2E suite uses a Supervisor-less testcontainer and adding a Supervisor sidecar is its own design discussion. A targeted test against a real HAOS instance (or a Supervisor-equipped Docker compose) would catch role/permission regressions at upstream Supervisor releases that current mocks won't.
  • Common Supervisor-REST helper extraction: _fetch_addon_logs (tools_bug_report.py), _restart_addon (settings_ui.py), and the new _supervisor_logs_get all hit http://supervisor/... with Authorization: Bearer ${SUPERVISOR_TOKEN} and slightly different error handling. Extracting a shared module-level helper (covered as alternative in [FEATURE] Standardize verify_ssl on direct-Supervisor httpx callers #1127's body) would consolidate the four-site pattern. Out of scope here — fits as a follow-up to refactor: pass verify_ssl to remaining direct-Supervisor httpx callers #1128 once that lands.
  • source="supervisor" semantic rename: the existing source value names a route ("via Supervisor proxy") while the new source="system_service" and the other four sources name data types. A rename to source="addon" would be semantically cleaner but is unrelated to [BUG] ha_get_logs(source="supervisor") returns 403 for every slug on add-on installs #1116 and would touch every caller; deferred per the project's enger-Scope policy.

Live-test plan (post-merge dev-channel deploy):

  • ha_get_logs(source="supervisor", slug="core_mosquitto") — pre-fix returns 403 (verified in this session against stable v7.4.1); post-deploy expected 200 with logs.
  • ha_get_logs(source="supervisor", slug="self") and slug="81f33d0f_ha_mcp" — same expectation.
  • ha_get_logs(source="system_service", slug="supervisor") and the other six service slugs — expected 200.
  • ha_get_logs(source="system_service", slug="invalid_service") — expected validation error before HTTP.
  • ha_get_logs(source="supervisor", slug="nonexistent_addon") — expected 404 with the slug-not-installed suggestion.

kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 6, 2026
…only, dev96

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Patch76
Patch76 requested a review from kingpanther13 May 6, 2026 11:42
@Patch76

Patch76 commented May 6, 2026

Copy link
Copy Markdown
Member Author

TL;DR for 4463d7d since the Implementation Summary above runs long:

  • 16 of your 17 CR items adopted; item 8 declined with rationale — level warning is already emitted at tools_utility.py:139 (supervisor is in the warn-tuple, pinned by test_level_param_emits_warning_for_supervisor_source). If the intent was harder than warn (e.g. raise on level+slug), that's a different design call — flag it.
  • Scope-add from your 12:42 comment landed: hassio_role: default → manager in both addon manifests + new source="system_service" for the seven /<service>/logs endpoints (slug-enum validated against {supervisor, host, core, dns, audio, multicast, observer}).
  • Pre-fix bug live-reproduced against stable v7.4.1 (core_mosquitto, 81f33d0f_ha_mcp, self all return 403). Post-merge dev-channel deploy re-runs the five-call test matrix from the Implementation Summary.

Full 17-item disposition: PR body table.

@kingpanther13 kingpanther13 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2 looks clean — 16 of 17 items addressed, item 8 decline is valid (the level warning is emitted from the tuple at tools_utility.py:160-167, now extended to system_service). Role bump + system_service source verified live against my test instance.

In addition to the above, please address:

Critical

  1. source="error_log" is also broken on Supervisor installs — same shape as #1116. HA Core's bootstrap.py (current dev) sets err_log_path = None when SUPERVISOR env is present, which means hass.data[DATA_LOGGING] is never set, and the APIErrorLog view registration is gated on if DATA_LOGGING in hass.data: — so /api/error_log is not registered at all on HA OS / Supervised, returning 404 by-design. Verified empirically and against home-assistant/core source. Fix shape parallels what you already built: when is_running_in_addon(), route error_log to _supervisor_logs_get("core") (HA Core's container log via Supervisor — same content, different transport). On non-addon installs keep the current /api/error_log path. Helper is already in place; this is a one-branch addition in client.get_error_log() plus a tool-layer note. Worth landing in this PR since it's the same root cause and same fix shape — closes the "log fetch doesn't work on Supervisor installs" class of bugs in one go.

While you're in there (nits, address inline)
2. slug param is silently ignored on non-supervisor/non-system_service sources. tools_utility.py:154-167 warns when entity_id/end_time are passed to non-logbook sources, and warns when level is passed to non-system/error_log sources. slug has no equivalent — passing slug="x" with source="logbook" is silently dropped. Add a third warning: if source not in ("supervisor", "system_service") and slug is not None:.
3. Role escalation is undocumented. hassio_role: default → manager is a meaningful permission bump (the addon can now start/stop/install/update other addons, read all their info, etc.). The addon-config comment is good for code reviewers but users updating the addon won't see it. One-line note in homeassistant-addon/DOCS.md (and the dev sibling per the dev-first translation rule) calling out the new role + why would close the doc gap.

… addon installs

HA Core's `bootstrap.py` sets `err_log_path = None` when the `SUPERVISOR`
env var is present (line 641-651 in current dev), so `hass.data[DATA_LOGGING]`
is never populated and the `APIErrorLog` view is not registered — meaning
`/api/error_log` returns 404 by-design on HA OS / Supervised installs.

Mirror the `get_addon_logs` fix shape (homeassistant-ai#1116): branch
`HomeAssistantClient.get_error_log()` on `is_running_in_addon()`. On the
addon branch route to `_supervisor_logs_get("core")` (HA Core's container
log via Supervisor — same content, different transport). Non-addon installs
keep the existing `/api/error_log` proxy path.

Also:
- Add the missing `slug` parameter incompatibility warning in
  `tools_utility.ha_get_logs`: parallels the existing `level` /
  `entity_id`/`end_time` warnings; previously `slug="x"` with a
  non-supervisor source was silently dropped.
- Document the addon's `hassio_role: manager` requirement in
  `homeassistant-addon/DOCS.md` (under Security) and the dev sibling
  `homeassistant-addon-dev/DOCS.md` (under Configuration → Permissions)
  so users updating the addon see the role bump rationale.

Tests:
- `TestGetErrorLogBranchSelection` — pins both branches of the gate.
- `TestSlugParameterIncompatibilityWarning` — parametrized over four
  non-supervisor sources (warn) and two supervisor sources (no warn).

Addresses kingpanther13's round-2 review on homeassistant-ai#1126.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Patch76

Patch76 commented May 6, 2026

Copy link
Copy Markdown
Member Author

Round-2 → Round-3 follow-up — addressed in a49dbc3:

Critical 1 (error_log Supervisor branch)HomeAssistantClient.get_error_log() now branches on is_running_in_addon() and routes to _supervisor_logs_get("core") for the addon path. HA Core's bootstrap.py:641-651 err_log_path = None behavior verified against current dev. TestGetErrorLogBranchSelection pins both branches.

Nit 2 (slug warning) — third warning branch in tools_utility.ha_get_logs for source not in ("supervisor", "system_service") and slug is not None. TestSlugParameterIncompatibilityWarning parametrizes over four non-supervisor sources and two supervisor sources.

Nit 3 (role docs)### Supervisor Permissions section in homeassistant-addon/DOCS.md (Security) + ### Permissions subsection in homeassistant-addon-dev/DOCS.md (Configuration), citing the role bump rationale and #1116.

Local: 1711 unit tests pass, ruff clean. PR description updated to reflect the extended scope.

@Patch76 Patch76 changed the title fix: route addon log fetches directly to supervisor on addon installs fix: route addon and HA core log fetches directly to Supervisor on addon installs May 6, 2026
kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 6, 2026
…only, dev97

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@kingpanther13 kingpanther13 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round-3 verified: error_log routes via Supervisor on addon installs, slug warning fires for non-supervisor sources, role-bump documented. All 17+3 review items addressed, tests added, live-confirmed on Fork-Dev. LGTM.

@Patch76
Patch76 merged commit 17c389e into homeassistant-ai:master May 6, 2026
15 checks passed
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

🧪 Your changes are now in the dev channel!

Your PR has been merged to master and is available for testing in the dev channel.

Test your changes before the next stable release (biweekly Wednesday):
📖 Dev Channel Documentation

Quick start

# Run dev version
uvx ha-mcp-dev

# Check version
uvx ha-mcp-dev --version

Docker:

docker pull ghcr.io/homeassistant-ai/ha-mcp:dev
docker run --rm -i \
  -e HOMEASSISTANT_URL=http://your-ha:8123 \
  -e HOMEASSISTANT_TOKEN=your_token \
  ghcr.io/homeassistant-ai/ha-mcp:dev

Found an issue? Please open a new bug report and mention this PR for context.

Patch76 added a commit to Patch76/ha-mcp that referenced this pull request May 7, 2026
Merge brings in homeassistant-ai#1126, homeassistant-ai#1135, homeassistant-ai#1136, homeassistant-ai#1138 and the dev-addon publish
chain since the branch's previous head `147ad5f`. Conflict in
`tests/src/unit/test_settings_ui.py` resolved by keeping both adjacent
additions: master's `test_returns_500_when_save_fails` (read-only-fs
500-surfacing test from homeassistant-ai#1138) inside `TestSaveToolsValidation`, plus
this PR's new `TestRestartAddon` class right after.

KP13 round-1 review asks (CHANGES_REQUESTED 2026-05-06 20:38 UTC) all
addressed:

1. **Narrow connection-drop catch** — the `except` tuple in
   `_restart_addon` (in `settings_ui.py`) is now
   `(httpx.ReadError, httpx.RemoteProtocolError)`. `httpx.ConnectError`
   is no longer treated as a successful restart; it falls through to
   the `httpx.HTTPError` handler returning 502 + `CONNECTION_FAILED`.
   Inline comment documents the deliberate exclusion (DNS /
   TCP-refused / supervisor-socket-misconfigured all mean Supervisor
   was unreachable, not that a restart was initiated).

2. **Parametrize connection-drop test** + separate `ConnectError` →
   502 case. `test_treats_connection_drop_as_success` now parametrizes
   over `(httpx.ReadError, httpx.RemoteProtocolError)`. New
   `test_connect_error_returns_502` locks the contract that a
   connection-failure-before-handshake surfaces as 502.

3. **Boy-Scout: pin remaining `_restart_addon` branches.** Two new
   tests: `test_generic_http_error_returns_502` (uses
   `httpx.PoolTimeout` to exercise the `httpx.HTTPError` fall-through)
   and `test_supervisor_4xx_returns_502` (Supervisor returns 401 →
   handler maps to 502).

4. **Symbol-based test docstrings** — class-docstring + method
   docstrings now reference "the `if not token:` guard", "the catch
   on `(ReadError, RemoteProtocolError)`", "the `httpx.HTTPError`
   handler", "the `status_code >= 400` branch" instead of line numbers
   that shift with every kwarg-split / refactor.

5. **Top-level `import httpx`** in `tests/src/unit/test_settings_ui.py`
   replaces the inline `__import__("httpx").ReadError(...)` workaround.

6. **Trim "post-G1 state"** from the `verify_ssl = True` fixture
   comment. Kept the substantive part ("must resolve to a real bool,
   not a MagicMock, because httpx accepts only bool/SSLContext for
   `verify=`") that pays off in 6 months.

7. **Move homeassistant-ai#960 cross-reference** out of the `TestRestartAddon` class
   docstring. Closed-PR review history rots fast in source; the PR
   body is the right place for it.

Local: 1762 unit tests pass, ruff lint + format clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch76 added a commit that referenced this pull request May 7, 2026
#1128)

* refactor: pass verify_ssl to remaining direct-Supervisor httpx callers

Closes #1127. Mirrors the verify=self.verify_ssl propagation pattern
established in #1126 (rest_client.py:_get_addon_logs_via_supervisor) at
the two other direct-Supervisor httpx call sites:

- tools_bug_report.py:_fetch_addon_logs uses get_global_settings().verify_ssl
  (module-level helper, no self/closure context).
- settings_ui.py:_restart_addon uses server.client.verify_ssl (closure has
  access to server: HomeAssistantSmartMCPServer).

Both paths effectively propagate Settings.verify_ssl via the access route
appropriate to each call site's scope. The http://supervisor URL is plain
HTTP and TLS-irrelevant in practice — the parameter keeps all three
constructor sites consistent with the established HomeAssistantClient
pattern.

* refactor: read verify_ssl from server.settings instead of server.client

Per Gemini review on PR #1128: server.client is a lazy @Property
(server.py) — accessing it for a single config bool would instantiate
the full HomeAssistantClient (httpx pool, settings re-read, log line)
on first access. server.settings is eager-initialized in the
HomeAssistantSmartMCPServer constructor and is the canonical source of
truth for verify_ssl.

Additional benefit: in OAuth deployment mode (__main__.py:868),
HomeAssistantSmartMCPServer is constructed with an OAuthProxyClient
whose __getattr__ proxies to a per-request OAuth client requiring an
authenticated request context. _restart_addon is a plain admin POST
without that context, so server.client.verify_ssl could have surfaced
as an auth error in OAuth mode. server.settings.verify_ssl sidesteps it
without depending on OAuthProxyClient's attribute-forwarding semantics.

* test: pin _restart_addon untested branches per Boy-Scout

Adds unit-test coverage for the two previously-untested branches in
settings_ui.py:_restart_addon:

- Missing SUPERVISOR_TOKEN (settings_ui.py:780-789) — non-addon installs
  hit this when the user clicks Restart against a Docker/pyinstaller
  setup; the structured 400 must surface rather than ever reaching the
  Supervisor URL.
- Connection-drop-as-success (settings_ui.py:798-801) — the Supervisor
  kills our process mid-request during a restart, so a ReadError /
  RemoteProtocolError / ConnectError from the POST is the documented
  success signal.

Mirrors the _capture_handler pattern from TestSaveToolsValidation. The
fixture-level server.settings.verify_ssl = True is required by this
PR's post-G1 access path (httpx accepts only bool/SSLContext for verify=).

Boy-Scout fix while already touching _restart_addon for the
verify_ssl-propagation refactor — closes the test-coverage gap I'd
flagged in #960's approve-body but never followed up on at the time.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch76 added a commit that referenced this pull request May 11, 2026
* refactor: extract shared Supervisor httpx client helper (#1130)

Three direct-Supervisor httpx call sites all built fresh AsyncClients with
the same boilerplate (base URL http://supervisor, Authorization: Bearer
$SUPERVISOR_TOKEN). Move that into a single factory in client/supervisor_client.py
and have the call sites pass relative paths instead of full URLs:

- client/rest_client.py:_supervisor_logs_get
- tools/tools_bug_report.py:_fetch_addon_logs
- settings_ui.py:_restart_addon

Per-call clients (vs. a singleton) preserved — these endpoints are
low-frequency, connection-pool reuse is negligible, and singleton lifecycle
adds shutdown/reload coupling that the issue's #1126 G2 review already
declined for the same reason. verify and timeout stay caller-supplied so
each site keeps its existing settings-source choice (instance snapshot vs.
live read). Token is read from env in the helper at construction time,
matching the original sites; the absent-token policy stays at the call
site (rich exception, silent empty string, or 400 JSONResponse — these
don't share a common shape and the helper shouldn't dictate one).

Tests: new tests/src/unit/test_supervisor_client.py covers the factory
contract (base_url, env override, Bearer header, timeout/verify
forwarding, absent-token graceful degradation). Two existing tests in
test_tools_utility_supervisor_logs.py updated to the new contract: URL
passed to .get() is now relative; Authorization header asserted on the
constructor kwargs instead of the per-call kwargs.

Closes #1130.

* fix: address round-2 review findings on #1130

- Raise RuntimeError on absent/empty SUPERVISOR_TOKEN at the factory so
  the malformed Bearer header never reaches Supervisor.
- Add wire-shape unit tests for _fetch_addon_logs and _restart_addon
  asserting relative URLs, ctor-set Authorization, and no per-call
  Authorization kwarg.
- Add a header-layering test pinning per-call Accept overlay on the
  ctor-set Authorization.
- Drop ephemeral PR/issue refs from code and test comments per
  AGENTS.md.
- Broaden the verify parameter type to httpx's full surface, add an
  env-read-at-construction Note to the docstring, dedup the
  token-handling paragraph.
- Sibling: test_config_toggles_section_renders_in_templates now
  delenv's SUPERVISOR_TOKEN so the SimpleNamespace fake (missing
  verify_ssl) doesn't hit the addon-logs path on containers with
  the env var set.

---------

Co-authored-by: Patch76 <mkglasmoor@gmail.com>
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request May 13, 2026
…→ 7.5.0) (#455)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/homeassistant-ai/ha-mcp](https://github.com/homeassistant-ai/ha-mcp) | minor | `7.4.0` → `7.5.0` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/3) for more information.

---

### Release Notes

<details>
<summary>homeassistant-ai/ha-mcp (ghcr.io/homeassistant-ai/ha-mcp)</summary>

### [`v7.5.0`](https://github.com/homeassistant-ai/ha-mcp/blob/HEAD/CHANGELOG.md#v750-2026-05-13)

[Compare Source](homeassistant-ai/ha-mcp@v7.4.0...v7.5.0)

##### Added

- Add ENABLE\_LITE\_DOCSTRINGS beta toggle
  ([#&#8203;1259](homeassistant-ai/ha-mcp#1259))
- Add ha\_call\_event tool for publishing events on the HA event bus ([#&#8203;996](homeassistant-ai/ha-mcp#996))
  ([#&#8203;1239](homeassistant-ai/ha-mcp#1239))
- Pinpoint backslash-escape mistake in python\_sandbox errors
  ([#&#8203;1204](homeassistant-ai/ha-mcp#1204))
- Reject empty-trigger automations targeting scene.create
  ([#&#8203;1187](homeassistant-ai/ha-mcp#1187))
- Add scene config tools — ha\_config\_get/set/remove\_scene
  ([#&#8203;1168](homeassistant-ai/ha-mcp#1168))
- **addon**: Optional OAuth 2.1 mode for webhook proxy (beta)
  ([#&#8203;1184](homeassistant-ai/ha-mcp#1184))
- Surface helper schema inline in ha\_config\_set\_helper validation errors ([#&#8203;1149](homeassistant-ai/ha-mcp#1149))
  ([#&#8203;1179](homeassistant-ai/ha-mcp#1179))
- Emit progress via FastMCP Context in long-running tools
  ([#&#8203;1124](homeassistant-ai/ha-mcp#1124))
- Broaden python\_transform AST allowlist + improve error UX
  ([#&#8203;1163](homeassistant-ai/ha-mcp#1163))
- Add ha\_manage\_custom\_tool — sandboxed code execution escape hatch
  ([#&#8203;854](homeassistant-ai/ha-mcp#854))
- Always-on skills; rename list/read resource tools with ha\_ prefix
  ([#&#8203;1136](homeassistant-ai/ha-mcp#1136))
- Expose device\_class + options on ha\_set\_entity / ha\_get\_entity (Show As)
  ([#&#8203;1135](homeassistant-ai/ha-mcp#1135))
- **site**: Inline wizard data into setup.astro, migrate setup nuggets, drop content collections
  ([#&#8203;1120](homeassistant-ai/ha-mcp#1120))
- Add "Advanced debug logging" toggle for kill-signal diagnostics
  ([#&#8203;1117](homeassistant-ai/ha-mcp#1117))
- **yaml**: Scoped lovelace.dashboards.\<url\_path> support (issue [#&#8203;1034](homeassistant-ai/ha-mcp#1034))
  ([#&#8203;1103](homeassistant-ai/ha-mcp#1103))
- Add HA\_VERIFY\_SSL toggle to disable TLS verification
  ([#&#8203;1104](homeassistant-ai/ha-mcp#1104))
- Per-top-level-key config\_hash for ha\_manage\_energy\_prefs ([#&#8203;1049](homeassistant-ai/ha-mcp#1049))
  ([#&#8203;1098](homeassistant-ai/ha-mcp#1098))
- **site**: Add gemini-cli setup notes + compose hardening to wizard ([#&#8203;1027](homeassistant-ai/ha-mcp#1027))
  ([#&#8203;1087](homeassistant-ai/ha-mcp#1087))
- Add convenience modes to ha\_manage\_energy\_prefs ([#&#8203;1050](homeassistant-ai/ha-mcp#1050))
  ([#&#8203;1073](homeassistant-ai/ha-mcp#1073))
- Surface integration log levels in ha\_get\_logs/integration/addon ([#&#8203;956](homeassistant-ai/ha-mcp#956))
  ([#&#8203;1003](homeassistant-ai/ha-mcp#1003))
- Expose allowlist\_external\_dirs in ha\_get\_overview full system\_info
  ([#&#8203;1053](homeassistant-ai/ha-mcp#1053))
- **dashboards**: Unify identifier handling in ha\_config\_\*\_dashboard tools ([#&#8203;981](homeassistant-ai/ha-mcp#981))
  ([#&#8203;1075](homeassistant-ai/ha-mcp#1075))
- Include addon container logs in bug reports
  ([#&#8203;934](homeassistant-ai/ha-mcp#934))
- Add WebSocket response-shaping controls to ha\_manage\_addon
  ([#&#8203;1009](homeassistant-ai/ha-mcp#1009))
- Web-based settings UI for per-tool enable/disable/pin
  ([#&#8203;960](homeassistant-ai/ha-mcp#960))
- **site**: Add OpenCode support to setup wizard
  ([#&#8203;1080](homeassistant-ai/ha-mcp#1080))

##### Changed

- Clarify standard-mode HTTP deployment guidance
  ([#&#8203;1185](homeassistant-ai/ha-mcp#1185))
- Add Cloudflared add-on hostname alternative for tunnel service
  ([#&#8203;1183](homeassistant-ai/ha-mcp#1183))
- Align tool naming convention between AGENTS.md and styleguide ([#&#8203;943](homeassistant-ai/ha-mcp#943))
  ([#&#8203;1174](homeassistant-ai/ha-mcp#1174))
- **addon**: Note tool-list ([#&#8203;985](homeassistant-ai/ha-mcp#985 divergence; fix [#&#8203;1139](https://github.com/homeassistant-ai/ha-mcp/issues/1139)/[#&#8203;1162](https://github.com/homeassistant-ai/ha-mcp/issues/1162) test conflict
  ([#&#8203;1172](homeassistant-ai/ha-mcp#1172))
- Add brew install option for mcp-proxy on macOS
  ([#&#8203;1171](homeassistant-ai/ha-mcp#1171))
- Update contributors list \[contributors-updated]
  ([`aba01a1`](homeassistant-ai/ha-mcp@aba01a1))
- Warn against enable\_tool\_search on Claude Sonnet/Opus ([#&#8203;1088](homeassistant-ai/ha-mcp#1088))
  ([#&#8203;1140](homeassistant-ai/ha-mcp#1140))
- Address [#&#8203;1094](homeassistant-ai/ha-mcp#1094) review nits on OpenCode mirror comments
  ([#&#8203;1105](homeassistant-ai/ha-mcp#1105))

##### Fixed

- **integrations**: Surface ConfigEntry.options via OptionsFlow probe
  ([#&#8203;1245](homeassistant-ai/ha-mcp#1245))
- **backup**: Discover local agent at call time instead of hardcoding hassio.local
  ([#&#8203;1244](homeassistant-ai/ha-mcp#1244))
- Triage all 10 ha\_search\_entities behaviors from [#&#8203;1170](homeassistant-ai/ha-mcp#1170)
  ([#&#8203;1195](homeassistant-ai/ha-mcp#1195))
- Replace cron with systemd for demo server (prevents process leak)
  ([#&#8203;1110](homeassistant-ai/ha-mcp#1110))
- Improve ha\_manage\_addon discoverability (BM25 keywords + slug examples)
  ([#&#8203;1200](homeassistant-ai/ha-mcp#1200))
- Route Supervisor 401s to structured tool errors + add E2E coverage ([#&#8203;1129](homeassistant-ai/ha-mcp#1129))
  ([#&#8203;1192](homeassistant-ai/ha-mcp#1192))
- Harden \_validate\_category\_id gate to cover dict-promoted category
  ([#&#8203;1190](homeassistant-ai/ha-mcp#1190))
- Broaden template anti-pattern detection + skill discoverability ([#&#8203;1011](homeassistant-ai/ha-mcp#1011))
  ([#&#8203;1181](homeassistant-ai/ha-mcp#1181))
- Return newest automation traces, add offset+order pagination ([#&#8203;1177](homeassistant-ai/ha-mcp#1177))
  ([#&#8203;1178](homeassistant-ai/ha-mcp#1178))
- **security**: Write YAML backups outside www/ (GHSA-g39v-cvjh-8fpf)
  ([#&#8203;1180](homeassistant-ai/ha-mcp#1180))
- **search**: Apply domain\_filter when area\_filter is set ([#&#8203;1162](homeassistant-ai/ha-mcp#1162))
  ([#&#8203;1165](homeassistant-ai/ha-mcp#1165))
- **resources**: Reject HA-config YAML in dashboard resource content
  ([#&#8203;1160](homeassistant-ai/ha-mcp#1160))
- Close 19 bugs in ha\_config\_set\_helper (issue [#&#8203;1150](homeassistant-ai/ha-mcp#1150))
  ([#&#8203;1151](homeassistant-ai/ha-mcp#1151))
- Route addon log fetches directly to supervisor on addon installs
  ([#&#8203;1126](homeassistant-ai/ha-mcp#1126))
- Survive read-only filesystems at startup
  ([#&#8203;1138](homeassistant-ai/ha-mcp#1138))
- **helpers**: Clarify name-required-on-create for ha\_config\_set\_helper
  ([#&#8203;1143](homeassistant-ai/ha-mcp#1143))
- Resolve disabled entities via entity\_registry in helper deletion
  ([#&#8203;1119](homeassistant-ai/ha-mcp#1119))
- Allow unary operators in python\_transform sandbox
  ([#&#8203;1118](homeassistant-ai/ha-mcp#1118))
- **site**: Add github-copilot-agents wizard branch + delete unreferenced data/clients.ts
  ([#&#8203;1108](homeassistant-ai/ha-mcp#1108))
- **addons**: Route addon API calls through HA Core ingress proxy
  ([#&#8203;1069](homeassistant-ai/ha-mcp#1069))
- **webhook-proxy**: Surface webhook registration failures instead of silently loading
  ([#&#8203;1101](homeassistant-ai/ha-mcp#1101))
- **site**: Resolve client display-order collisions and anchor OpenCode shape
  ([#&#8203;1094](homeassistant-ai/ha-mcp#1094))

##### Performance Improvements

- Dedupe lovelace/dashboards/list in ha\_config\_set\_dashboard ([#&#8203;1085](homeassistant-ai/ha-mcp#1085))
  ([#&#8203;1191](homeassistant-ai/ha-mcp#1191))

##### Refactoring

- Drop obsolete ha\_mcp\_tools defensive ruamel.yaml imports ([post-#&#8203;1268](https://github.com/post-/ha-mcp/issues/1268))
  ([#&#8203;1269](homeassistant-ai/ha-mcp#1269))
- Extract shared Supervisor httpx client helper ([#&#8203;1130](homeassistant-ai/ha-mcp#1130))
  ([#&#8203;1203](homeassistant-ai/ha-mcp#1203))
- Surface client identity, AI model, config toggles, and prompt context in ha\_report\_issue
  ([#&#8203;1189](homeassistant-ai/ha-mcp#1189))
- Harden Context injection with safe-emit + branch coverage
  ([#&#8203;1173](homeassistant-ai/ha-mcp#1173))
- Consolidate area/floor set+remove tools (revisit of [#&#8203;813](homeassistant-ai/ha-mcp#813))
  ([#&#8203;1139](homeassistant-ai/ha-mcp#1139))
- Pass verify\_ssl to remaining direct-Supervisor httpx callers
  ([#&#8203;1128](homeassistant-ai/ha-mcp#1128))
- Validate only new entries on convenience-mode writes ([#&#8203;1086](homeassistant-ai/ha-mcp#1086))
  ([#&#8203;1100](homeassistant-ai/ha-mcp#1100))

***

<details>
<summary>Internal Changes</summary>

##### Fixed

- **ci**: Align pr.yml E2E with --dist loadscope ([#&#8203;1206](homeassistant-ai/ha-mcp#1206))
  ([#&#8203;1247](homeassistant-ai/ha-mcp#1247))
- **ci**: Switch Renovate to a GitHub App token to allow workflow-file pushes
  ([#&#8203;1229](homeassistant-ai/ha-mcp#1229))
- **ci**: Break gemini-triage retrigger loop and bump turn budget
  ([#&#8203;1131](homeassistant-ai/ha-mcp#1131))
- **ci**: Harden gemini-triage so failures stop spamming user issues
  ([#&#8203;1122](homeassistant-ai/ha-mcp#1122))
- **ci**: Unbreak hotfix-release semantic-release run
  ([#&#8203;1091](homeassistant-ai/ha-mcp#1091))

##### Chores

- **addon**: Publish dev addon version 7.4.1.dev299 \[skip ci]
  ([`397aa6d`](homeassistant-ai/ha-mcp@397aa6d))
- **addon**: Publish dev addon version 7.4.1.dev298 \[skip ci]
  ([`942b7e0`](homeassistant-ai/ha-mcp@942b7e0))
- Sync tool docs after merge \[skip ci]
  ([`6823c47`](homeassistant-ai/ha-mcp@6823c47))
- **addon**: Publish dev addon version 7.4.1.dev297 \[skip ci]
  ([`6eac062`](homeassistant-ai/ha-mcp@6eac062))
- **addon**: Publish dev addon version 7.4.1.dev296 \[skip ci]
  ([`b2afe93`](homeassistant-ai/ha-mcp@b2afe93))
- **addon**: Publish dev addon version 7.4.1.dev295 \[skip ci]
  ([`4f4c4f3`](homeassistant-ai/ha-mcp@4f4c4f3))
- **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.5.1
  ([#&#8203;1236](homeassistant-ai/ha-mcp#1236))
- **addon**: Publish dev addon version 7.4.1.dev294 \[skip ci]
  ([`fd24991`](homeassistant-ai/ha-mcp@fd24991))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.13
  ([#&#8203;1233](homeassistant-ai/ha-mcp#1233))
- **addon**: Publish dev addon version 7.4.1.dev293 \[skip ci]
  ([`fcc6496`](homeassistant-ai/ha-mcp@fcc6496))
- **addon**: Publish dev addon version 7.4.1.dev292 \[skip ci]
  ([`2961650`](homeassistant-ai/ha-mcp@2961650))
- **addon**: Publish dev addon version 7.4.1.dev291 \[skip ci]
  ([`5703112`](homeassistant-ai/ha-mcp@5703112))
- **addon**: Publish dev addon version 7.4.1.dev290 \[skip ci]
  ([`19b2f65`](homeassistant-ai/ha-mcp@19b2f65))
- **addon**: Publish dev addon version 7.4.1.dev289 \[skip ci]
  ([`e5a1365`](homeassistant-ai/ha-mcp@e5a1365))
- Sync tool docs after merge \[skip ci]
  ([`d2ff93b`](homeassistant-ai/ha-mcp@d2ff93b))
- **addon**: Publish dev addon version 7.4.1.dev288 \[skip ci]
  ([`0f62400`](homeassistant-ai/ha-mcp@0f62400))
- Sync tool docs after merge \[skip ci]
  ([`c7e2066`](homeassistant-ai/ha-mcp@c7e2066))
- **addon**: Publish dev addon version 7.4.1.dev287 \[skip ci]
  ([`c1133d4`](homeassistant-ai/ha-mcp@c1133d4))
- **addon**: Publish dev addon version 7.4.1.dev286 \[skip ci]
  ([`1ae790e`](homeassistant-ai/ha-mcp@1ae790e))
- **addon**: Publish dev addon version 7.4.1.dev285 \[skip ci]
  ([`2387d0c`](homeassistant-ai/ha-mcp@2387d0c))
- **addon**: Publish dev addon version 7.4.1.dev284 \[skip ci]
  ([`dd3a4a5`](homeassistant-ai/ha-mcp@dd3a4a5))
- **addon**: Publish dev addon version 7.4.1.dev283 \[skip ci]
  ([`78af8eb`](homeassistant-ai/ha-mcp@78af8eb))
- Sync tool docs after merge \[skip ci]
  ([`093fd74`](homeassistant-ai/ha-mcp@093fd74))
- **addon**: Publish dev addon version 7.4.1.dev282 \[skip ci]
  ([`2141e15`](homeassistant-ai/ha-mcp@2141e15))
- Sync tool docs after merge \[skip ci]
  ([`7810c95`](homeassistant-ai/ha-mcp@7810c95))
- **addon**: Publish dev addon version 7.4.1.dev281 \[skip ci]
  ([`7d79ec2`](homeassistant-ai/ha-mcp@7d79ec2))
- Sync tool docs after merge \[skip ci]
  ([`a73dc81`](homeassistant-ai/ha-mcp@a73dc81))
- **addon**: Publish dev addon version 7.4.1.dev280 \[skip ci]
  ([`c858ce3`](homeassistant-ai/ha-mcp@c858ce3))
- Sync tool docs after merge \[skip ci]
  ([`a587be0`](homeassistant-ai/ha-mcp@a587be0))
- **addon**: Publish dev addon version 7.4.1.dev279 \[skip ci]
  ([`b78ddb2`](homeassistant-ai/ha-mcp@b78ddb2))
- Sync tool docs after merge \[skip ci]
  ([`1210725`](homeassistant-ai/ha-mcp@1210725))
- **addon**: Publish dev addon version 7.4.1.dev278 \[skip ci]
  ([`a282c17`](homeassistant-ai/ha-mcp@a282c17))
- **addon**: Publish dev addon version 7.4.1.dev277 \[skip ci]
  ([`1081768`](homeassistant-ai/ha-mcp@1081768))
- Sync tool docs after merge \[skip ci]
  ([`e03f5d2`](homeassistant-ai/ha-mcp@e03f5d2))
- **addon**: Publish dev addon version 7.4.1.dev276 \[skip ci]
  ([`c4ef680`](homeassistant-ai/ha-mcp@c4ef680))
- **addon**: Publish dev addon version 7.4.1.dev275 \[skip ci]
  ([`780422d`](homeassistant-ai/ha-mcp@780422d))
- Sync tool docs after merge \[skip ci]
  ([`8a2bd1a`](homeassistant-ai/ha-mcp@8a2bd1a))
- **addon**: Publish dev addon version 7.4.1.dev274 \[skip ci]
  ([`f0f09de`](homeassistant-ai/ha-mcp@f0f09de))
- **addon**: Publish dev addon version 7.4.1.dev273 \[skip ci]
  ([`cb49f68`](homeassistant-ai/ha-mcp@cb49f68))
- **addon**: Publish dev addon version 7.4.1.dev272 \[skip ci]
  ([`5097186`](homeassistant-ai/ha-mcp@5097186))
- **addon**: Publish dev addon version 7.4.1.dev271 \[skip ci]
  ([`4714342`](homeassistant-ai/ha-mcp@4714342))
- **addon**: Publish dev addon version 7.4.1.dev270 \[skip ci]
  ([`217982a`](homeassistant-ai/ha-mcp@217982a))
- **addon**: Publish dev addon version 7.4.1.dev269 \[skip ci]
  ([`a65dd5f`](homeassistant-ai/ha-mcp@a65dd5f))
- Sync tool docs after merge \[skip ci]
  ([`0e6b54f`](homeassistant-ai/ha-mcp@0e6b54f))
- **addon**: Publish dev addon version 7.4.1.dev268 \[skip ci]
  ([`60ba1f2`](homeassistant-ai/ha-mcp@60ba1f2))
- **addon**: Publish dev addon version 7.4.1.dev267 \[skip ci]
  ([`13412aa`](homeassistant-ai/ha-mcp@13412aa))
- Sync tool docs after merge \[skip ci]
  ([`2702a0f`](homeassistant-ai/ha-mcp@2702a0f))
- **addon**: Publish dev addon version 7.4.1.dev266 \[skip ci]
  ([`77abe0b`](homeassistant-ai/ha-mcp@77abe0b))
- **addon**: Publish dev addon version 7.4.1.dev265 \[skip ci]
  ([`08b69db`](homeassistant-ai/ha-mcp@08b69db))
- Sync tool docs after merge \[skip ci]
  ([`c1f24b5`](homeassistant-ai/ha-mcp@c1f24b5))
- **addon**: Publish dev addon version 7.4.1.dev264 \[skip ci]
  ([`f2583f6`](homeassistant-ai/ha-mcp@f2583f6))
- Sync tool docs after merge \[skip ci]
  ([`c2ed2d3`](homeassistant-ai/ha-mcp@c2ed2d3))
- **addon**: Publish dev addon version 7.4.1.dev263 \[skip ci]
  ([`9d43e54`](homeassistant-ai/ha-mcp@9d43e54))
- **addon**: Publish dev addon version 7.4.1.dev262 \[skip ci]
  ([`a7355c8`](homeassistant-ai/ha-mcp@a7355c8))
- Sync tool docs after merge \[skip ci]
  ([`085bd8a`](homeassistant-ai/ha-mcp@085bd8a))
- Convert agents to skills
  ([#&#8203;1084](homeassistant-ai/ha-mcp#1084))
- **addon**: Publish dev addon version 7.4.1.dev261 \[skip ci]
  ([`0d1af36`](homeassistant-ai/ha-mcp@0d1af36))
- **addon**: Publish dev addon version 7.4.1.dev260 \[skip ci]
  ([`29397dc`](homeassistant-ai/ha-mcp@29397dc))
- **addon**: Publish dev addon version 7.4.1.dev259 \[skip ci]
  ([`4bbc74b`](homeassistant-ai/ha-mcp@4bbc74b))
- Sync tool docs after merge \[skip ci]
  ([`0f6d41e`](homeassistant-ai/ha-mcp@0f6d41e))
- **addon**: Publish dev addon version 7.4.1.dev258 \[skip ci]
  ([`6751d08`](homeassistant-ai/ha-mcp@6751d08))
- **addon**: Publish dev addon version 7.4.1.dev257 \[skip ci]
  ([`2213c89`](homeassistant-ai/ha-mcp@2213c89))
- **addon**: Publish dev addon version 7.4.1.dev256 \[skip ci]
  ([`18a366e`](homeassistant-ai/ha-mcp@18a366e))
- **addon**: Publish dev addon version 7.4.1.dev255 \[skip ci]
  ([`0e9b18d`](homeassistant-ai/ha-mcp@0e9b18d))
- **addon**: Publish dev addon version 7.4.1.dev254 \[skip ci]
  ([`39fc65b`](homeassistant-ai/ha-mcp@39fc65b))
- Sync tool docs after merge \[skip ci]
  ([`9fa0aea`](homeassistant-ai/ha-mcp@9fa0aea))
- **addon**: Publish dev addon version 7.4.1.dev253 \[skip ci]
  ([`0dcc59e`](homeassistant-ai/ha-mcp@0dcc59e))
- Sync tool docs after merge \[skip ci]
  ([`ec7413f`](homeassistant-ai/ha-mcp@ec7413f))
- **addon**: Publish dev addon version 7.4.1.dev252 \[skip ci]
  ([`345640c`](homeassistant-ai/ha-mcp@345640c))
- **addon**: Publish dev addon version 7.4.1.dev251 \[skip ci]
  ([`bab9d49`](homeassistant-ai/ha-mcp@bab9d49))
- Sync tool docs after merge \[skip ci]
  ([`726f0a5`](homeassistant-ai/ha-mcp@726f0a5))
- **addon**: Publish dev addon version 7.4.1.dev250 \[skip ci]
  ([`ded04ea`](homeassistant-ai/ha-mcp@ded04ea))
- **addon**: Publish dev addon version 7.4.1.dev249 \[skip ci]
  ([`37d5628`](homeassistant-ai/ha-mcp@37d5628))
- **addon**: Publish dev addon version 7.4.1.dev248 \[skip ci]
  ([`530786a`](homeassistant-ai/ha-mcp@530786a))
- Sync tool docs after merge \[skip ci]
  ([`36719c3`](homeassistant-ai/ha-mcp@36719c3))
- **addon**: Publish dev addon version 7.4.1.dev247 \[skip ci]
  ([`4dc47b5`](homeassistant-ai/ha-mcp@4dc47b5))
- **addon**: Publish dev addon version 7.4.1.dev246 \[skip ci]
  ([`6ffbd6a`](homeassistant-ai/ha-mcp@6ffbd6a))
- Sync tool docs after merge \[skip ci]
  ([`add66e3`](homeassistant-ai/ha-mcp@add66e3))
- **addon**: Publish dev addon version 7.4.1.dev245 \[skip ci]
  ([`d0114af`](homeassistant-ai/ha-mcp@d0114af))
- Sync tool docs after merge \[skip ci]
  ([`0ca41af`](homeassistant-ai/ha-mcp@0ca41af))
- **addon**: Publish dev addon version 7.4.1.dev244 \[skip ci]
  ([`d052dd0`](homeassistant-ai/ha-mcp@d052dd0))
- **addon**: Publish dev addon version 7.4.0.dev243 \[skip ci]
  ([`827bc65`](homeassistant-ai/ha-mcp@827bc65))
- Bump package version to 7.4.1 to match released addon
  ([`4f65497`](homeassistant-ai/ha-mcp@4f65497))
- **addon**: Publish dev addon version 7.4.0.dev242 \[skip ci]
  ([`8ba80ae`](homeassistant-ai/ha-mcp@8ba80ae))
- **addon**: Publish hotfix version 7.4.1
  ([`bda75e6`](homeassistant-ai/ha-mcp@bda75e6))
- **addon**: Publish dev addon version 7.4.0.dev241 \[skip ci]
  ([`2126428`](homeassistant-ai/ha-mcp@2126428))

##### Continuous Integration

- **deps**: Bump renovatebot/github-action in the github-actions group
  ([#&#8203;1218](homeassistant-ai/ha-mcp#1218))
- **deps**: Bump renovatebot/github-action in the github-actions group
  ([#&#8203;1111](homeassistant-ai/ha-mcp#1111))

##### Refactoring

- Extract \_fetch\_dashboards\_list helper ([#&#8203;1193](homeassistant-ai/ha-mcp#1193))
  ([#&#8203;1207](homeassistant-ai/ha-mcp#1207))

##### Testing

- **e2e**: Module-scope bulk\_automations + bulk\_scripts fixtures (refs [#&#8203;366](homeassistant-ai/ha-mcp#366))
  ([#&#8203;1275](homeassistant-ai/ha-mcp#1275))
- **e2e**: Lower INPUT\_BOOLEAN\_WAIT from 30s to 10s (refs [#&#8203;366](homeassistant-ai/ha-mcp#366))
  ([#&#8203;1273](homeassistant-ai/ha-mcp#1273))
- **e2e**: Generalize readiness-gate diagnostics helper (closes [#&#8203;1267](homeassistant-ai/ha-mcp#1267))
  ([#&#8203;1271](homeassistant-ai/ha-mcp#1271))
- **e2e**: Narrow except clauses in e2e polling helpers (closes [#&#8203;1266](homeassistant-ai/ha-mcp#1266))
  ([#&#8203;1270](homeassistant-ai/ha-mcp#1270))
- **e2e**: Drop ha\_mcp\_tools retry-path + pre-install manifest requirements
  ([#&#8203;1268](homeassistant-ai/ha-mcp#1268))
- **e2e**: Instrument and retry ha\_mcp\_tools readiness wait
  ([#&#8203;1262](homeassistant-ai/ha-mcp#1262))
- Use time.monotonic() in UAT runner and test\_env\_manager
  ([#&#8203;1254](homeassistant-ai/ha-mcp#1254))
- **e2e**: Detect partial/corrupt hacs\_frontend dir in fast-path guard
  ([#&#8203;1253](homeassistant-ai/ha-mcp#1253))
- **e2e**: Remove unused wait/assert helpers ([post-#&#8203;1249](https://github.com/post-/ha-mcp/issues/1249) audit)
  ([#&#8203;1256](homeassistant-ai/ha-mcp#1256))
- **e2e**: Clear stale .hacs\_frontend.lock from prior crashed runs
  ([#&#8203;1252](homeassistant-ai/ha-mcp#1252))
- **e2e**: Use time.monotonic() in workflow polling loops
  ([#&#8203;1258](homeassistant-ai/ha-mcp#1258))
- **e2e**: Use time.monotonic() for duration polling ([#&#8203;1234](homeassistant-ai/ha-mcp#1234))
  ([#&#8203;1249](homeassistant-ai/ha-mcp#1249))
- **e2e**: Close ARM ha\_mcp\_tools readiness race under loadscope
  ([#&#8203;1208](homeassistant-ai/ha-mcp#1208))
- **hacs**: Tighten is\_hacs\_unavailable to not match legitimate "Repository not found"
  ([#&#8203;1246](homeassistant-ai/ha-mcp#1246))
- **seed**: Unblock 3 silent-skip pagination/state tests via baked recorder DB
  ([#&#8203;1240](homeassistant-ai/ha-mcp#1240))
- **seed**: Register a writable local\_calendar to unblock event-creation test
  ([#&#8203;1243](homeassistant-ai/ha-mcp#1243))
- **addon**: Fix base64 padding-bit flake in token tamper tests ([#&#8203;1238](homeassistant-ai/ha-mcp#1238))
  ([#&#8203;1241](homeassistant-ai/ha-mcp#1241))
- **seed**: Add a writable scene for test\_call\_service\_scene\_turn\_on
  ([#&#8203;1231](homeassistant-ai/ha-mcp#1231))
- **seed**: Assign demo device to living\_room area for filter test
  ([#&#8203;1230](homeassistant-ai/ha-mcp#1230))
- **e2e**: Drop nonexistent sun service from session readiness wait
  ([#&#8203;1227](homeassistant-ai/ha-mcp#1227))
- **e2e**: Self-contain dashboard register/remove to fix ARM xdist race ([#&#8203;1196](homeassistant-ai/ha-mcp#1196))
  ([#&#8203;1201](homeassistant-ai/ha-mcp#1201))
- Fix EN dash in docstring causing RUF002 lint failure
  ([`eac5916`](homeassistant-ai/ha-mcp@eac5916))
- Address Gemini review feedback on host detection and port allocation
  ([`960305e`](homeassistant-ai/ha-mcp@960305e))
- Fix three categories of E2E test flakiness
  ([`39417ff`](homeassistant-ai/ha-mcp@39417ff))
- **e2e**: Pin config\_hash stability for dashboards
  ([#&#8203;1132](homeassistant-ai/ha-mcp#1132))

</details>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/455
doonga added a commit to greyrock-labs/home-ops that referenced this pull request May 13, 2026
….0 ) (#26)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/homeassistant-ai/ha-mcp](https://github.com/homeassistant-ai/ha-mcp) | minor | `7.4.0` → `7.5.0` |

---

### Release Notes

<details>
<summary>homeassistant-ai/ha-mcp (ghcr.io/homeassistant-ai/ha-mcp)</summary>

### [`v7.5.0`](https://github.com/homeassistant-ai/ha-mcp/blob/HEAD/CHANGELOG.md#v750-2026-05-13)

[Compare Source](homeassistant-ai/ha-mcp@v7.4.0...v7.5.0)

##### Added

- Add ENABLE\_LITE\_DOCSTRINGS beta toggle
  ([#&#8203;1259](homeassistant-ai/ha-mcp#1259))
- Add ha\_call\_event tool for publishing events on the HA event bus ([#&#8203;996](homeassistant-ai/ha-mcp#996))
  ([#&#8203;1239](homeassistant-ai/ha-mcp#1239))
- Pinpoint backslash-escape mistake in python\_sandbox errors
  ([#&#8203;1204](homeassistant-ai/ha-mcp#1204))
- Reject empty-trigger automations targeting scene.create
  ([#&#8203;1187](homeassistant-ai/ha-mcp#1187))
- Add scene config tools — ha\_config\_get/set/remove\_scene
  ([#&#8203;1168](homeassistant-ai/ha-mcp#1168))
- **addon**: Optional OAuth 2.1 mode for webhook proxy (beta)
  ([#&#8203;1184](homeassistant-ai/ha-mcp#1184))
- Surface helper schema inline in ha\_config\_set\_helper validation errors ([#&#8203;1149](homeassistant-ai/ha-mcp#1149))
  ([#&#8203;1179](homeassistant-ai/ha-mcp#1179))
- Emit progress via FastMCP Context in long-running tools
  ([#&#8203;1124](homeassistant-ai/ha-mcp#1124))
- Broaden python\_transform AST allowlist + improve error UX
  ([#&#8203;1163](homeassistant-ai/ha-mcp#1163))
- Add ha\_manage\_custom\_tool — sandboxed code execution escape hatch
  ([#&#8203;854](homeassistant-ai/ha-mcp#854))
- Always-on skills; rename list/read resource tools with ha\_ prefix
  ([#&#8203;1136](homeassistant-ai/ha-mcp#1136))
- Expose device\_class + options on ha\_set\_entity / ha\_get\_entity (Show As)
  ([#&#8203;1135](homeassistant-ai/ha-mcp#1135))
- **site**: Inline wizard data into setup.astro, migrate setup nuggets, drop content collections
  ([#&#8203;1120](homeassistant-ai/ha-mcp#1120))
- Add "Advanced debug logging" toggle for kill-signal diagnostics
  ([#&#8203;1117](homeassistant-ai/ha-mcp#1117))
- **yaml**: Scoped lovelace.dashboards.\<url\_path> support (issue [#&#8203;1034](homeassistant-ai/ha-mcp#1034))
  ([#&#8203;1103](homeassistant-ai/ha-mcp#1103))
- Add HA\_VERIFY\_SSL toggle to disable TLS verification
  ([#&#8203;1104](homeassistant-ai/ha-mcp#1104))
- Per-top-level-key config\_hash for ha\_manage\_energy\_prefs ([#&#8203;1049](homeassistant-ai/ha-mcp#1049))
  ([#&#8203;1098](homeassistant-ai/ha-mcp#1098))
- **site**: Add gemini-cli setup notes + compose hardening to wizard ([#&#8203;1027](homeassistant-ai/ha-mcp#1027))
  ([#&#8203;1087](homeassistant-ai/ha-mcp#1087))
- Add convenience modes to ha\_manage\_energy\_prefs ([#&#8203;1050](homeassistant-ai/ha-mcp#1050))
  ([#&#8203;1073](homeassistant-ai/ha-mcp#1073))
- Surface integration log levels in ha\_get\_logs/integration/addon ([#&#8203;956](homeassistant-ai/ha-mcp#956))
  ([#&#8203;1003](homeassistant-ai/ha-mcp#1003))
- Expose allowlist\_external\_dirs in ha\_get\_overview full system\_info
  ([#&#8203;1053](homeassistant-ai/ha-mcp#1053))
- **dashboards**: Unify identifier handling in ha\_config\_\*\_dashboard tools ([#&#8203;981](homeassistant-ai/ha-mcp#981))
  ([#&#8203;1075](homeassistant-ai/ha-mcp#1075))
- Include addon container logs in bug reports
  ([#&#8203;934](homeassistant-ai/ha-mcp#934))
- Add WebSocket response-shaping controls to ha\_manage\_addon
  ([#&#8203;1009](homeassistant-ai/ha-mcp#1009))
- Web-based settings UI for per-tool enable/disable/pin
  ([#&#8203;960](homeassistant-ai/ha-mcp#960))
- **site**: Add OpenCode support to setup wizard
  ([#&#8203;1080](homeassistant-ai/ha-mcp#1080))

##### Changed

- Clarify standard-mode HTTP deployment guidance
  ([#&#8203;1185](homeassistant-ai/ha-mcp#1185))
- Add Cloudflared add-on hostname alternative for tunnel service
  ([#&#8203;1183](homeassistant-ai/ha-mcp#1183))
- Align tool naming convention between AGENTS.md and styleguide ([#&#8203;943](homeassistant-ai/ha-mcp#943))
  ([#&#8203;1174](homeassistant-ai/ha-mcp#1174))
- **addon**: Note tool-list ([#&#8203;985](homeassistant-ai/ha-mcp#985 divergence; fix [#&#8203;1139](https://github.com/homeassistant-ai/ha-mcp/issues/1139)/[#&#8203;1162](https://github.com/homeassistant-ai/ha-mcp/issues/1162) test conflict
  ([#&#8203;1172](homeassistant-ai/ha-mcp#1172))
- Add brew install option for mcp-proxy on macOS
  ([#&#8203;1171](homeassistant-ai/ha-mcp#1171))
- Update contributors list \[contributors-updated]
  ([`aba01a1`](homeassistant-ai/ha-mcp@aba01a1))
- Warn against enable\_tool\_search on Claude Sonnet/Opus ([#&#8203;1088](homeassistant-ai/ha-mcp#1088))
  ([#&#8203;1140](homeassistant-ai/ha-mcp#1140))
- Address [#&#8203;1094](homeassistant-ai/ha-mcp#1094) review nits on OpenCode mirror comments
  ([#&#8203;1105](homeassistant-ai/ha-mcp#1105))

##### Fixed

- **integrations**: Surface ConfigEntry.options via OptionsFlow probe
  ([#&#8203;1245](homeassistant-ai/ha-mcp#1245))
- **backup**: Discover local agent at call time instead of hardcoding hassio.local
  ([#&#8203;1244](homeassistant-ai/ha-mcp#1244))
- Triage all 10 ha\_search\_entities behaviors from [#&#8203;1170](homeassistant-ai/ha-mcp#1170)
  ([#&#8203;1195](homeassistant-ai/ha-mcp#1195))
- Replace cron with systemd for demo server (prevents process leak)
  ([#&#8203;1110](homeassistant-ai/ha-mcp#1110))
- Improve ha\_manage\_addon discoverability (BM25 keywords + slug examples)
  ([#&#8203;1200](homeassistant-ai/ha-mcp#1200))
- Route Supervisor 401s to structured tool errors + add E2E coverage ([#&#8203;1129](homeassistant-ai/ha-mcp#1129))
  ([#&#8203;1192](homeassistant-ai/ha-mcp#1192))
- Harden \_validate\_category\_id gate to cover dict-promoted category
  ([#&#8203;1190](homeassistant-ai/ha-mcp#1190))
- Broaden template anti-pattern detection + skill discoverability ([#&#8203;1011](homeassistant-ai/ha-mcp#1011))
  ([#&#8203;1181](homeassistant-ai/ha-mcp#1181))
- Return newest automation traces, add offset+order pagination ([#&#8203;1177](homeassistant-ai/ha-mcp#1177))
  ([#&#8203;1178](homeassistant-ai/ha-mcp#1178))
- **security**: Write YAML backups outside www/ (GHSA-g39v-cvjh-8fpf)
  ([#&#8203;1180](homeassistant-ai/ha-mcp#1180))
- **search**: Apply domain\_filter when area\_filter is set ([#&#8203;1162](homeassistant-ai/ha-mcp#1162))
  ([#&#8203;1165](homeassistant-ai/ha-mcp#1165))
- **resources**: Reject HA-config YAML in dashboard resource content
  ([#&#8203;1160](homeassistant-ai/ha-mcp#1160))
- Close 19 bugs in ha\_config\_set\_helper (issue [#&#8203;1150](homeassistant-ai/ha-mcp#1150))
  ([#&#8203;1151](homeassistant-ai/ha-mcp#1151))
- Route addon log fetches directly to supervisor on addon installs
  ([#&#8203;1126](homeassistant-ai/ha-mcp#1126))
- Survive read-only filesystems at startup
  ([#&#8203;1138](homeassistant-ai/ha-mcp#1138))
- **helpers**: Clarify name-required-on-create for ha\_config\_set\_helper
  ([#&#8203;1143](homeassistant-ai/ha-mcp#1143))
- Resolve disabled entities via entity\_registry in helper deletion
  ([#&#8203;1119](homeassistant-ai/ha-mcp#1119))
- Allow unary operators in python\_transform sandbox
  ([#&#8203;1118](homeassistant-ai/ha-mcp#1118))
- **site**: Add github-copilot-agents wizard branch + delete unreferenced data/clients.ts
  ([#&#8203;1108](homeassistant-ai/ha-mcp#1108))
- **addons**: Route addon API calls through HA Core ingress proxy
  ([#&#8203;1069](homeassistant-ai/ha-mcp#1069))
- **webhook-proxy**: Surface webhook registration failures instead of silently loading
  ([#&#8203;1101](homeassistant-ai/ha-mcp#1101))
- **site**: Resolve client display-order collisions and anchor OpenCode shape
  ([#&#8203;1094](homeassistant-ai/ha-mcp#1094))

##### Performance Improvements

- Dedupe lovelace/dashboards/list in ha\_config\_set\_dashboard ([#&#8203;1085](homeassistant-ai/ha-mcp#1085))
  ([#&#8203;1191](homeassistant-ai/ha-mcp#1191))

##### Refactoring

- Drop obsolete ha\_mcp\_tools defensive ruamel.yaml imports ([post-#&#8203;1268](https://github.com/post-/ha-mcp/issues/1268))
  ([#&#8203;1269](homeassistant-ai/ha-mcp#1269))
- Extract shared Supervisor httpx client helper ([#&#8203;1130](homeassistant-ai/ha-mcp#1130))
  ([#&#8203;1203](homeassistant-ai/ha-mcp#1203))
- Surface client identity, AI model, config toggles, and prompt context in ha\_report\_issue
  ([#&#8203;1189](homeassistant-ai/ha-mcp#1189))
- Harden Context injection with safe-emit + branch coverage
  ([#&#8203;1173](homeassistant-ai/ha-mcp#1173))
- Consolidate area/floor set+remove tools (revisit of [#&#8203;813](homeassistant-ai/ha-mcp#813))
  ([#&#8203;1139](homeassistant-ai/ha-mcp#1139))
- Pass verify\_ssl to remaining direct-Supervisor httpx callers
  ([#&#8203;1128](homeassistant-ai/ha-mcp#1128))
- Validate only new entries on convenience-mode writes ([#&#8203;1086](homeassistant-ai/ha-mcp#1086))
  ([#&#8203;1100](homeassistant-ai/ha-mcp#1100))

***

<details>
<summary>Internal Changes</summary>

##### Fixed

- **ci**: Align pr.yml E2E with --dist loadscope ([#&#8203;1206](homeassistant-ai/ha-mcp#1206))
  ([#&#8203;1247](homeassistant-ai/ha-mcp#1247))
- **ci**: Switch Renovate to a GitHub App token to allow workflow-file pushes
  ([#&#8203;1229](homeassistant-ai/ha-mcp#1229))
- **ci**: Break gemini-triage retrigger loop and bump turn budget
  ([#&#8203;1131](homeassistant-ai/ha-mcp#1131))
- **ci**: Harden gemini-triage so failures stop spamming user issues
  ([#&#8203;1122](homeassistant-ai/ha-mcp#1122))
- **ci**: Unbreak hotfix-release semantic-release run
  ([#&#8203;1091](homeassistant-ai/ha-mcp#1091))

##### Chores

- **addon**: Publish dev addon version 7.4.1.dev299 \[skip ci]
  ([`397aa6d`](homeassistant-ai/ha-mcp@397aa6d))
- **addon**: Publish dev addon version 7.4.1.dev298 \[skip ci]
  ([`942b7e0`](homeassistant-ai/ha-mcp@942b7e0))
- Sync tool docs after merge \[skip ci]
  ([`6823c47`](homeassistant-ai/ha-mcp@6823c47))
- **addon**: Publish dev addon version 7.4.1.dev297 \[skip ci]
  ([`6eac062`](homeassistant-ai/ha-mcp@6eac062))
- **addon**: Publish dev addon version 7.4.1.dev296 \[skip ci]
  ([`b2afe93`](homeassistant-ai/ha-mcp@b2afe93))
- **addon**: Publish dev addon version 7.4.1.dev295 \[skip ci]
  ([`4f4c4f3`](homeassistant-ai/ha-mcp@4f4c4f3))
- **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.5.1
  ([#&#8203;1236](homeassistant-ai/ha-mcp#1236))
- **addon**: Publish dev addon version 7.4.1.dev294 \[skip ci]
  ([`fd24991`](homeassistant-ai/ha-mcp@fd24991))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.13
  ([#&#8203;1233](homeassistant-ai/ha-mcp#1233))
- **addon**: Publish dev addon version 7.4.1.dev293 \[skip ci]
  ([`fcc6496`](homeassistant-ai/ha-mcp@fcc6496))
- **addon**: Publish dev addon version 7.4.1.dev292 \[skip ci]
  ([`2961650`](homeassistant-ai/ha-mcp@2961650))
- **addon**: Publish dev addon version 7.4.1.dev291 \[skip ci]
  ([`5703112`](homeassistant-ai/ha-mcp@5703112))
- **addon**: Publish dev addon version 7.4.1.dev290 \[skip ci]
  ([`19b2f65`](homeassistant-ai/ha-mcp@19b2f65))
- **addon**: Publish dev addon version 7.4.1.dev289 \[skip ci]
  ([`e5a1365`](homeassistant-ai/ha-mcp@e5a1365))
- Sync tool docs after merge \[skip ci]
  ([`d2ff93b`](homeassistant-ai/ha-mcp@d2ff93b))
- **addon**: Publish dev addon version 7.4.1.dev288 \[skip ci]
  ([`0f62400`](homeassistant-ai/ha-mcp@0f62400))
- Sync tool docs after merge \[skip ci]
  ([`c7e2066`](homeassistant-ai/ha-mcp@c7e2066))
- **addon**: Publish dev addon version 7.4.1.dev287 \[skip ci]
  ([`c1133d4`](homeassistant-ai/ha-mcp@c1133d4))
- **addon**: Publish dev addon version 7.4.1.dev286 \[skip ci]
  ([`1ae790e`](homeassistant-ai/ha-mcp@1ae790e))
- **addon**: Publish dev addon version 7.4.1.dev285 \[skip ci]
  ([`2387d0c`](homeassistant-ai/ha-mcp@2387d0c))
- **addon**: Publish dev addon version 7.4.1.dev284 \[skip ci]
  ([`dd3a4a5`](homeassistant-ai/ha-mcp@dd3a4a5))
- **addon**: Publish dev addon version 7.4.1.dev283 \[skip ci]
  ([`78af8eb`](homeassistant-ai/ha-mcp@78af8eb))
- Sync tool docs after merge \[skip ci]
  ([`093fd74`](homeassistant-ai/ha-mcp@093fd74))
- **addon**: Publish dev addon version 7.4.1.dev282 \[skip ci]
  ([`2141e15`](homeassistant-ai/ha-mcp@2141e15))
- Sync tool docs after merge \[skip ci]
  ([`7810c95`](homeassistant-ai/ha-mcp@7810c95))
- **addon**: Publish dev addon version 7.4.1.dev281 \[skip ci]
  ([`7d79ec2`](homeassistant-ai/ha-mcp@7d79ec2))
- Sync tool docs after merge \[skip ci]
  ([`a73dc81`](homeassistant-ai/ha-mcp@a73dc81))
- **addon**: Publish dev addon version 7.4.1.dev280 \[skip ci]
  ([`c858ce3`](homeassistant-ai/ha-mcp@c858ce3))
- Sync tool docs after merge \[skip ci]
  ([`a587be0`](homeassistant-ai/ha-mcp@a587be0))
- **addon**: Publish dev addon version 7.4.1.dev279 \[skip ci]
  ([`b78ddb2`](homeassistant-ai/ha-mcp@b78ddb2))
- Sync tool docs after merge \[skip ci]
  ([`1210725`](homeassistant-ai/ha-mcp@1210725))
- **addon**: Publish dev addon version 7.4.1.dev278 \[skip ci]
  ([`a282c17`](homeassistant-ai/ha-mcp@a282c17))
- **addon**: Publish dev addon version 7.4.1.dev277 \[skip ci]
  ([`1081768`](homeassistant-ai/ha-mcp@1081768))
- Sync tool docs after merge \[skip ci]
  ([`e03f5d2`](homeassistant-ai/ha-mcp@e03f5d2))
- **addon**: Publish dev addon version 7.4.1.dev276 \[skip ci]
  ([`c4ef680`](homeassistant-ai/ha-mcp@c4ef680))
- **addon**: Publish dev addon version 7.4.1.dev275 \[skip ci]
  ([`780422d`](homeassistant-ai/ha-mcp@780422d))
- Sync tool docs after merge \[skip ci]
  ([`8a2bd1a`](homeassistant-ai/ha-mcp@8a2bd1a))
- **addon**: Publish dev addon version 7.4.1.dev274 \[skip ci]
  ([`f0f09de`](homeassistant-ai/ha-mcp@f0f09de))
- **addon**: Publish dev addon version 7.4.1.dev273 \[skip ci]
  ([`cb49f68`](homeassistant-ai/ha-mcp@cb49f68))
- **addon**: Publish dev addon version 7.4.1.dev272 \[skip ci]
  ([`5097186`](homeassistant-ai/ha-mcp@5097186))
- **addon**: Publish dev addon version 7.4.1.dev271 \[skip ci]
  ([`4714342`](homeassistant-ai/ha-mcp@4714342))
- **addon**: Publish dev addon version 7.4.1.dev270 \[skip ci]
  ([`217982a`](homeassistant-ai/ha-mcp@217982a))
- **addon**: Publish dev addon version 7.4.1.dev269 \[skip ci]
  ([`a65dd5f`](homeassistant-ai/ha-mcp@a65dd5f))
- Sync tool docs after merge \[skip ci]
  ([`0e6b54f`](homeassistant-ai/ha-mcp@0e6b54f))
- **addon**: Publish dev addon version 7.4.1.dev268 \[skip ci]
  ([`60ba1f2`](homeassistant-ai/ha-mcp@60ba1f2))
- **addon**: Publish dev addon version 7.4.1.dev267 \[skip ci]
  ([`13412aa`](homeassistant-ai/ha-mcp@13412aa))
- Sync tool docs after merge \[skip ci]
  ([`2702a0f`](homeassistant-ai/ha-mcp@2702a0f))
- **addon**: Publish dev addon version 7.4.1.dev266 \[skip ci]
  ([`77abe0b`](homeassistant-ai/ha-mcp@77abe0b))
- **addon**: Publish dev addon version 7.4.1.dev265 \[skip ci]
  ([`08b69db`](homeassistant-ai/ha-mcp@08b69db))
- Sync tool docs after merge \[skip ci]
  ([`c1f24b5`](homeassistant-ai/ha-mcp@c1f24b5))
- **addon**: Publish dev addon version 7.4.1.dev264 \[skip ci]
  ([`f2583f6`](homeassistant-ai/ha-mcp@f2583f6))
- Sync tool docs after merge \[skip ci]
  ([`c2ed2d3`](homeassistant-ai/ha-mcp@c2ed2d3))
- **addon**: Publish dev addon version 7.4.1.dev263 \[skip ci]
  ([`9d43e54`](homeassistant-ai/ha-mcp@9d43e54))
- **addon**: Publish dev addon version 7.4.1.dev262 \[skip ci]
  ([`a7355c8`](homeassistant-ai/ha-mcp@a7355c8))
- Sync tool docs after merge \[skip ci]
  ([`085bd8a`](homeassistant-ai/ha-mcp@085bd8a))
- Convert agents to skills
  ([#&#8203;1084](homeassistant-ai/ha-mcp#1084))
- **addon**: Publish dev addon version 7.4.1.dev261 \[skip ci]
  ([`0d1af36`](homeassistant-ai/ha-mcp@0d1af36))
- **addon**: Publish dev addon version 7.4.1.dev260 \[skip ci]
  ([`29397dc`](homeassistant-ai/ha-mcp@29397dc))
- **addon**: Publish dev addon version 7.4.1.dev259 \[skip ci]
  ([`4bbc74b`](homeassistant-ai/ha-mcp@4bbc74b))
- Sync tool docs after merge \[skip ci]
  ([`0f6d41e`](homeassistant-ai/ha-mcp@0f6d41e))
- **addon**: Publish dev addon version 7.4.1.dev258 \[skip ci]
  ([`6751d08`](homeassistant-ai/ha-mcp@6751d08))
- **addon**: Publish dev addon version 7.4.1.dev257 \[skip ci]
  ([`2213c89`](homeassistant-ai/ha-mcp@2213c89))
- **addon**: Publish dev addon version 7.4.1.dev256 \[skip ci]
  ([`18a366e`](homeassistant-ai/ha-mcp@18a366e))
- **addon**: Publish dev addon version 7.4.1.dev255 \[skip ci]
  ([`0e9b18d`](homeassistant-ai/ha-mcp@0e9b18d))
- **addon**: Publish dev addon version 7.4.1.dev254 \[skip ci]
  ([`39fc65b`](homeassistant-ai/ha-mcp@39fc65b))
- Sync tool docs after merge \[skip ci]
  ([`9fa0aea`](homeassistant-ai/ha-mcp@9fa0aea))
- **addon**: Publish dev addon version 7.4.1.dev253 \[skip ci]
  ([`0dcc59e`](homeassistant-ai/ha-mcp@0dcc59e))
- Sync tool docs after merge \[skip ci]
  ([`ec7413f`](homeassistant-ai/ha-mcp@ec7413f))
- **addon**: Publish dev addon version 7.4.1.dev252 \[skip ci]
  ([`345640c`](homeassistant-ai/ha-mcp@345640c))
- **addon**: Publish dev addon version 7.4.1.dev251 \[skip ci]
  ([`bab9d49`](homeassistant-ai/ha-mcp@bab9d49))
- Sync tool docs after merge \[skip ci]
  ([`726f0a5`](homeassistant-ai/ha-mcp@726f0a5))
- **addon**: Publish dev addon version 7.4.1.dev250 \[skip ci]
  ([`ded04ea`](homeassistant-ai/ha-mcp@ded04ea))
- **addon**: Publish dev addon version 7.4.1.dev249 \[skip ci]
  ([`37d5628`](homeassistant-ai/ha-mcp@37d5628))
- **addon**: Publish dev addon version 7.4.1.dev248 \[skip ci]
  ([`530786a`](homeassistant-ai/ha-mcp@530786a))
- Sync tool docs after merge \[skip ci]
  ([`36719c3`](homeassistant-ai/ha-mcp@36719c3))
- **addon**: Publish dev addon version 7.4.1.dev247 \[skip ci]
  ([`4dc47b5`](homeassistant-ai/ha-mcp@4dc47b5))
- **addon**: Publish dev addon version 7.4.1.dev246 \[skip ci]
  ([`6ffbd6a`](homeassistant-ai/ha-mcp@6ffbd6a))
- Sync tool docs after merge \[skip ci]
  ([`add66e3`](homeassistant-ai/ha-mcp@add66e3))
- **addon**: Publish dev addon version 7.4.1.dev245 \[skip ci]
  ([`d0114af`](homeassistant-ai/ha-mcp@d0114af))
- Sync tool docs after merge \[skip ci]
  ([`0ca41af`](homeassistant-ai/ha-mcp@0ca41af))
- **addon**: Publish dev addon version 7.4.1.dev244 \[skip ci]
  ([`d052dd0`](homeassistant-ai/ha-mcp@d052dd0))
- **addon**: Publish dev addon version 7.4.0.dev243 \[skip ci]
  ([`827bc65`](homeassistant-ai/ha-mcp@827bc65))
- Bump package version to 7.4.1 to match released addon
  ([`4f65497`](homeassistant-ai/ha-mcp@4f65497))
- **addon**: Publish dev addon version 7.4.0.dev242 \[skip ci]
  ([`8ba80ae`](homeassistant-ai/ha-mcp@8ba80ae))
- **addon**: Publish hotfix version 7.4.1
  ([`bda75e6`](homeassistant-ai/ha-mcp@bda75e6))
- **addon**: Publish dev addon version 7.4.0.dev241 \[skip ci]
  ([`2126428`](homeassistant-ai/ha-mcp@2126428))

##### Continuous Integration

- **deps**: Bump renovatebot/github-action in the github-actions group
  ([#&#8203;1218](homeassistant-ai/ha-mcp#1218))
- **deps**: Bump renovatebot/github-action in the github-actions group
  ([#&#8203;1111](homeassistant-ai/ha-mcp#1111))

##### Refactoring

- Extract \_fetch\_dashboards\_list helper ([#&#8203;1193](homeassistant-ai/ha-mcp#1193))
  ([#&#8203;1207](homeassistant-ai/ha-mcp#1207))

##### Testing

- **e2e**: Module-scope bulk\_automations + bulk\_scripts fixtures (refs [#&#8203;366](homeassistant-ai/ha-mcp#366))
  ([#&#8203;1275](homeassistant-ai/ha-mcp#1275))
- **e2e**: Lower INPUT\_BOOLEAN\_WAIT from 30s to 10s (refs [#&#8203;366](homeassistant-ai/ha-mcp#366))
  ([#&#8203;1273](homeassistant-ai/ha-mcp#1273))
- **e2e**: Generalize readiness-gate diagnostics helper (closes [#&#8203;1267](homeassistant-ai/ha-mcp#1267))
  ([#&#8203;1271](homeassistant-ai/ha-mcp#1271))
- **e2e**: Narrow except clauses in e2e polling helpers (closes [#&#8203;1266](homeassistant-ai/ha-mcp#1266))
  ([#&#8203;1270](homeassistant-ai/ha-mcp#1270))
- **e2e**: Drop ha\_mcp\_tools retry-path + pre-install manifest requirements
  ([#&#8203;1268](homeassistant-ai/ha-mcp#1268))
- **e2e**: Instrument and retry ha\_mcp\_tools readiness wait
  ([#&#8203;1262](homeassistant-ai/ha-mcp#1262))
- Use time.monotonic() in UAT runner and test\_env\_manager
  ([#&#8203;1254](homeassistant-ai/ha-mcp#1254))
- **e2e**: Detect partial/corrupt hacs\_frontend dir in fast-path guard
  ([#&#8203;1253](homeassistant-ai/ha-mcp#1253))
- **e2e**: Remove unused wait/assert helpers ([post-#&#8203;1249](https://github.com/post-/ha-mcp/issues/1249) audit)
  ([#&#8203;1256](homeassistant-ai/ha-mcp#1256))
- **e2e**: Clear stale .hacs\_frontend.lock from prior crashed runs
  ([#&#8203;1252](homeassistant-ai/ha-mcp#1252))
- **e2e**: Use time.monotonic() in workflow polling loops
  ([#&#8203;1258](homeassistant-ai/ha-mcp#1258))
- **e2e**: Use time.monotonic() for duration polling ([#&#8203;1234](homeassistant-ai/ha-mcp#1234))
  ([#&#8203;1249](homeassistant-ai/ha-mcp#1249))
- **e2e**: Close ARM ha\_mcp\_tools readiness race under loadscope
  ([#&#8203;1208](homeassistant-ai/ha-mcp#1208))
- **hacs**: Tighten is\_hacs\_unavailable to not match legitimate "Repository not found"
  ([#&#8203;1246](homeassistant-ai/ha-mcp#1246))
- **seed**: Unblock 3 silent-skip pagination/state tests via baked recorder DB
  ([#&#8203;1240](homeassistant-ai/ha-mcp#1240))
- **seed**: Register a writable local\_calendar to unblock event-creation test
  ([#&#8203;1243](homeassistant-ai/ha-mcp#1243))
- **addon**: Fix base64 padding-bit flake in token tamper tests ([#&#8203;1238](homeassistant-ai/ha-mcp#1238))
  ([#&#8203;1241](homeassistant-ai/ha-mcp#1241))
- **seed**: Add a writable scene for test\_call\_service\_scene\_turn\_on
  ([#&#8203;1231](homeassistant-ai/ha-mcp#1231))
- **seed**: Assign demo device to living\_room area for filter test
  ([#&#8203;1230](homeassistant-ai/ha-mcp#1230))
- **e2e**: Drop nonexistent sun service from session readiness wait
  ([#&#8203;1227](homeassistant-ai/ha-mcp#1227))
- **e2e**: Self-contain dashboard register/remove to fix ARM xdist race ([#&#8203;1196](homeassistant-ai/ha-mcp#1196))
  ([#&#8203;1201](homeassistant-ai/ha-mcp#1201))
- Fix EN dash in docstring causing RUF002 lint failure
  ([`eac5916`](homeassistant-ai/ha-mcp@eac5916))
- Address Gemini review feedback on host detection and port allocation
  ([`960305e`](homeassistant-ai/ha-mcp@960305e))
- Fix three categories of E2E test flakiness
  ([`39417ff`](homeassistant-ai/ha-mcp@39417ff))
- **e2e**: Pin config\_hash stability for dashboards
  ([#&#8203;1132](homeassistant-ai/ha-mcp#1132))

</details>

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/New_York)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNyIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Co-authored-by: todd <tpunderson@greyrock.io>
Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/26
kingpanther13 added a commit that referenced this pull request May 15, 2026
… non-addon installs (#1283)

* fix: HA Core proxy fallback for ha_get_logs(source=system_service) on non-addon installs

Closes #1260. Pre-fix `_get_system_service_logs` had only the Supervisor-direct
branch, so non-addon installs (the Docker image, uvx ha-mcp, pip-based deploys)
fell straight through to the SUPERVISOR_TOKEN-absent fail-fast in
`_supervisor_logs_get` for every system-service slug. Sibling `get_addon_logs`
and `get_error_log` already had the `is_running_in_addon()` gate plus HA Core
proxy fallback added in PR #1126; this PR closes the parallel gap.

The reproduced symptom matched the issue exactly on a non-addon install
(uvx ha-mcp pointed at a Supervisor-equipped HA): `source="supervisor"` worked
via the existing Core proxy fallback, while `source="system_service"` with
slug in {supervisor, host, core} returned AUTH_INVALID_TOKEN with the
misleading "addon-mode gate fired but SUPERVISOR_TOKEN env var not set"
message. Once the gate is added, that fail-fast string is accurate again
because the only callers reaching it are addon-mode-confirmed.

Verified the proxy path is reachable for all seven service slugs by reading
HA Core's `homeassistant/components/hassio/http.py` — PATHS_ADMIN whitelists
`{audio,cli,core,dns,host,multicast,observer,supervisor}/logs` plus
`addons/{slug}/logs`. Admin LLA is sufficient. (HA Core also proxies
`cli/logs`, which ha-mcp's SYSTEM_SERVICE_SLUGS doesn't include — leaving
that out as scope for a separate PR if anyone wants CLI logs surfaced.)

Tests: new TestGetSystemServiceLogsBranchSelection class mirrors the
existing TestGetAddonLogsBranchSelection / TestGetErrorLogBranchSelection
shape — non-addon branch parametrized over all seven slugs, plus an
addon-branch test that proxy is not called.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(rest_client): Accept header + 404 path on system_service non-addon branch

Two parity additions in TestGetSystemServiceLogsBranchSelection, per PR
review by pr-test-analyzer:

- Parametrized happy-path test now asserts `Accept: text/plain` on the
  proxy request. Without it the HA Core proxy negotiates application/json
  and the body stops being raw log text (same silent-failure signature
  #950 describes one layer up).
- New `test_non_addon_install_404_raises_api_error_with_service_context`
  anchors the live "observer returned 404 on hubs that don't run it"
  case from the #1260 end-to-end verification. Guards against a future
  refactor wrapping the proxy call in a swallow-and-return-empty
  try/except in `_get_system_service_logs`. Parallels
  `TestGetAddonLogs::test_raises_api_error_on_404_with_slug_context`.

64 tests pass in this file (was 63), ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(get_logs): branch-aware error suggestions + add cli to system_service slugs

Two follow-ons from the PR #1283 review, both addressing review findings
that were initially deferred as "future improvements" but are real
behavior issues introduced or exposed by this PR's branch split:

1. Branch-aware wrapper suggestions in tools_utility.py:

   Pre-fix, the AuthError and 403 branches in `_get_supervisor_log` and
   `_get_system_service_log` always emitted SUPERVISOR_TOKEN/hassio_role
   hints — useless on non-addon installs that hit the new HA Core proxy
   path. With #1283's gate-and-fallback split, the wrapper now gates on
   `is_running_in_addon()` and picks branch-appropriate suggestions:

   - In-addon AuthError → "Verify SUPERVISOR_TOKEN..." (unchanged)
   - Non-addon AuthError → "Verify HOMEASSISTANT_TOKEN is a valid admin
     Long-Lived Access Token..."
   - In-addon 403 (system_service) → "Addon's hassio_role must be
     'manager'..." (unchanged)
   - Non-addon 403 (system_service) → "The LLA must belong to an admin..."

   Same shape applied to `_get_supervisor_log` so source="supervisor"
   doesn't have the same dead-end advice on Docker/uvx installs either.

2. Add `cli` to SYSTEM_SERVICE_SLUGS:

   HA Core's hassio HTTP proxy already whitelists `cli/logs` in
   PATHS_ADMIN. Adding the slug surfaces Supervisor CLI logs via the
   same routes #1116 set up for the other seven services. Eight slugs
   total now.

Tests:
- Parametrized non-addon proxy test extended to 8 slugs (cli added)
- `test_all_seven_allowed_services_dispatch` renamed → `_all_allowed_`,
  extended to 8 slugs
- Existing `test_403_role_hint_suggestion` renamed to clarify it's the
  in-addon case + explicit `is_running_in_addon` mock added
- 5 new tests pinning the branch-aware suggestion behavior (in-addon
  AuthError, non-addon AuthError, non-addon 403) on both wrappers
- Supervisor mock updated to recognize the 8-slug set
- Backtick consistency in TestGetAddonLogsBranchSelection +
  TestGetSystemServiceLogsBranchSelection class docstrings, per
  comment-analyzer S3

71 unit tests pass in test_tools_utility_supervisor_logs.py (was 63 pre-PR,
64 after first review-pass test additions). Ruff clean across all
touched files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Patch76
Patch76 deleted the fix/issue-1116-supervisor-token-direct-endpoint branch May 15, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] ha_get_logs(source="supervisor") returns 403 for every slug on add-on installs

2 participants