Skip to content

fix: triage all 10 ha_search_entities behaviors from #1170#1195

Merged
kingpanther13 merged 12 commits into
homeassistant-ai:masterfrom
kingpanther13:issue-1170
May 11, 2026
Merged

fix: triage all 10 ha_search_entities behaviors from #1170#1195
kingpanther13 merged 12 commits into
homeassistant-ai:masterfrom
kingpanther13:issue-1170

Conversation

@kingpanther13
Copy link
Copy Markdown
Member

@kingpanther13 kingpanther13 commented May 10, 2026

What does this PR do?

Triage and fix every one of the 10 behaviors in ha_search_entities documented in #1170. Each finding is addressed with a targeted change plus regression tests; the alias work also closes #1166.

The 10 findings

# Finding Resolution
1 domain_filter is case-sensitive (silent zero on Light) normalize to .strip().lower() at both the tool and service-layer boundaries (and BEFORE the at-least-one-set validation, so a whitespace-only filter fails validation instead of falling through); the response echoes the canonical value
2 bedlight ties light.bed_light against five unrelated *_lights at score 76 per-entity, the entity_id tail and friendly_name now contribute their separator-stripped concat as a high-IDF BM25 token
3 area_only results miss score + match_type; essential_attributes only on fuzzy_search add score=100, match_type="area_match" so the shape matches the other four search-type branches; drop essential_attributes from fuzzy_search for full shape parity
4 area_filtered_query echoes area_filter per-result (asymmetric) drop the redundant per-result echo (top-level area_filter still present)
5 Threshold lets xyz_irrelevant_garbagecover.garage_door at score 92; lit surfaces 186 *_lite* entities at 85 multi-token coverage gate (≥50% of distinct tokens must fuzzy-match a doc token) + single-token min-length gate (≥4 chars) in typo_fallback. Legitimate typos like ligth (5 chars) unaffected
6 _partial_results_search returned a useless score-0 entity dump deleted; exceptions propagate so callers see the real cause instead of partial: true noise
7 area_only returns the first matched area only (and from a set, so non-deterministic) aggregate all matched areas in deterministic order; new area_names: list[str] joins legacy area_name. Exact id/name/alias matches short-circuit fuzzy aggregation so a literal area_id no longer leaks sibling areas' entities
8 Entity / area aliases not searchable fold entity_registry.aliases into the BM25 corpus via a single batched get_entries call; consult area_registry.aliases in the area resolver. Closes #1166.
9 Hidden entities (hidden_by) outrank visible peers under identical match conditions option (c) from the issue: hidden entities still surface, but receive a 20-point score penalty so visible matches sort above them. New include_hidden: bool = True parameter is the explicit opt-out for filtering them entirely
10 E2E coverage gap on populated areas new TestSearchEntitiesSeededAreasIssue1170 test class against the tests/initial_test_state seed

Implementation notes

  • Concat-token elision (fix: documentation formatting and accuracy improvements #2): the new _strip_separators helper exposes elided forms (bed_lightbedlight) as BM25 tokens. Single-token queries that omit separators now hit a unique high-IDF token directly instead of falling through to typo_fallback's tie-cluster.
  • Coverage gate + single-token gate (docs: fix typos and formatting in README #5): in _typo_fallback, multi-token queries must have ≥50% of their distinct tokens fuzzy-match some doc token. Garbage queries with one accidental hit (garbage~garage) are rejected. Single-token queries shorter than 4 chars also skip the fallback ("lit" no longer surfaces every *_lite* entity at score 85); legitimate typos like "ligth" are unaffected.
  • Aliases plumbing (feat: add detail_level parameter to ha_get_overview with 4 levels #8): the slim config/entity_registry/list deliberately omits aliases — we now batch-fetch via config/entity_registry/get_entries after the hidden filter, so the extra round-trip stays bounded by the post-filter survivor set. Same approach in get_entities_by_area for area-resolved entities. Alias-driven matches surface as match_type="alias_match".
  • Aggregate areas + exact-id short-circuit (feat: update repository references to homeassistant-ai/ha-mcp #7): the area_only branch was using next(iter(area_result["areas"].values())) against a set upstream — non-deterministic AND lossy. Now sorts by area_id and iterates all. area_names: list[str] is the new field; area_name (singular) is retained as the first match for one minor version of soft-compat. Exact id/name/alias matches now short-circuit fuzzy aggregation: area_filter="bedroom_kids" no longer fuzzy-matches its parent "bedroom" (partial_ratio=100) and aggregates sibling areas' entities.
  • Hidden-score penalty (feat: add backup creation and restore tools #9): new apply_hidden_penalty(score, hidden_by) helper in fuzzy_search.py subtracts a 20-point penalty whenever hidden_by is non-None. Wired through every branch that emits a score field — BM25, typo_fallback, exact_match, area_only, area_filtered_query, domain_listing — so the contract is uniform: a hidden exact-id match (raw 100 → 80) still ranks above a fuzzy threshold-floor visible match (60-70), but loses to any visible entity scoring 80+. The threshold gate runs on the raw score so a borderline hidden match doesn't silently disappear. include_hidden=False retains the explicit opt-out for callers that need visible-only results.
  • Error propagation (refactor: split ha_manage_* into ha_config_{get,set,remove}_* tools #6): the fuzzy fallback wrapper now has an except ToolError: raise guard, so structured failures from smart_entity_search propagate instead of being retried via the substring fallback. Alias-enrichment warnings carry a structured alias_enrichment_failed prefix and the survivor count for log bucketing.
  • CancelledError propagation: asyncio.gather(return_exceptions=True) captures asyncio.CancelledError like any other exception. Three call sites (smart_entity_search, _exact_match_search, the empty-query+domain branch) now explicitly re-raise it so the canceller doesn't wait forever.
  • Sort tie-breaker: all four score-only sort sites (fuzzy_search main path, _exact_match_search, area_only, domain_listing) now tie-break on entity_id for stable pagination — the hidden-penalty banding (visible@100, hidden@80) made the within-tier reordering particularly likely without the secondary key.
  • Validation-order fix: domain_filter / area_filter strip+lowercase now happens BEFORE the at-least-one-set validation. Previously a whitespace-only filter (" ") passed validation truthy then collapsed to "" and fell through to a silent zero-result fuzzy search.
  • Validation error suggestions: ValueError from coerce_bool_param / coerce_int_param is now caught separately and surfaced via create_validation_error with the helper's own message and no boilerplate operational suggestions (previously limit=0 returned "Check Home Assistant connection" next to "limit must be at least 1, got 0").
  • Internal-field strip helpers: strip_internal_fields (recursive, mutating, cycle-guarded) and public_fields (shallow, non-mutating) in tools/util_helpers.py centralise the leading-underscore convention so _hidden_by / _aliases enrichments don't leak through public tool returns. server.py:get_entities_by_area bridge now strips its delegate's output.
  • Zero-overlap message (area+domain): when area_filter resolves to populated areas but domain_filter wipes out every entity, the response now carries "No <domain> entities found in area: <area>" — previously the empty-area branch had a message but the populated-but-domain-filtered-empty branch was silent.

Public API impact

Type of change

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

Testing

  • I have tested these changes with a LLM agent
  • All automated tests pass (uv run pytest)
  • Code follows style guidelines (uv run ruff check)

Test additions:

  • TestFuzzySearcherIssue1170 in tests/src/unit/test_bm25_search.py: concat-token elision, multi-token coverage gate, alias-match labeling, single-token typo recall, alias-vs-name precedence, short single-token min-length gate ("lit" returns empty; "ligth" still recalls).
  • TestSmartEntitySearchPropagation — locks down the finding-6 contract that get_states failures surface as ToolError.
  • TestHiddenScorePenalty — locks down the option-(c) ranking contract: helper math, visible-vs-hidden ordering on shared queries, zero-clamp boundary, threshold-on-raw-score edge case (raw-100 hidden surfaces at score 80 even when threshold=100), typo_fallback penalty, CancelledError propagation, sort tie-breaker ordering.
  • TestStripInternalFields and TestPublicFields in tests/src/unit/test_util_helpers_internal_strip.py — full coverage of the leak-guard helpers (recursion, cycles, non-string keys, mutation semantics, shallow-copy contract).
  • TestExactMatchSearchCancelledPropagation in test_search_fallback.py_exact_match_search re-raises CancelledError from the registry task.
  • tests/src/unit/test_server_bridge_strip.pyserver.py:get_entities_by_area bridge actually invokes strip_internal_fields.
  • E2E regression tests in tests/src/e2e/tools/test_search_entities.py: one per runtime-observable finding, plus area-alias resolution, concat-token elision through the full pipeline, area_filtered_query + hidden penalty, domain_listing + hidden penalty, determinism of area_names ordering, exact area_id short-circuits fuzzy aggregation, shape consistency across all 5 search_types, validation error has no generic suggestions, area+domain zero-overlap message, parametrised whitespace test for domain_filter (5 variants), whitespace-only filter rejected at validation, hidden-with-penalty / include_hidden=False pair across substring + fuzzy + area_only branches.
  • TestSearchEntitiesSeededAreasIssue1170 class with 3 test methods exercising area_filter branches at realistic seeded-area populations (feat: Docker deployment and Home Assistant add-on support #10).
  • Existing unit tests in test_search_fallback.py rewritten for option (c) — assert hidden entities are present with reduced score, and assert include_hidden=False filters them out.
  • seeded_bedroom fixture wrapped in try/finally so a fixture-body failure (or pytest.skip) doesn't leak the seed area assignments.

Test updates:

  • valid_search_types in the response-structure regression test no longer accepts the deleted partial_listing.
  • test_search_fallback.py and test_search_pagination.py updated to drop references to the deleted _partial_results_search.

Future improvements

None tracked — every triage item from #1170, plus all findings surfaced during live stress testing on a real HA instance, is addressed in this PR.

Checklist

  • I have updated documentation if needed (tool docstrings refreshed inline; no public docs needed updating)

🤖 Generated with Claude Code

kingpanther13 and others added 2 commits May 9, 2026 23:37
…#1170

Closes homeassistant-ai#1170. Closes homeassistant-ai#1166.

Each finding from the umbrella issue is addressed:

  1. domain_filter case-insensitive — silent zero-result on "Light" was
     normalized away at the boundary; the response echoes the canonical
     lowercase value.
  2. Token elision — `bedlight` no longer ties bed_light against five
     unrelated `*_lights` at score 76. Per-entity, the entity_id tail
     and friendly_name now contribute their separator-stripped concat
     as a single high-IDF BM25 token.
  3. area_only response shape — results carry `score=100` and
     `match_type="area_match"` to match the four other search-type
     branches.
  4. Per-result `area_filter` echo dropped on `area_filtered_query` —
     top-level field still echoes; per-result was redundant and
     asymmetric vs the other branches.
  5. Multi-token coverage gate — `xyz_irrelevant_garbage` no longer
     surfaces `cover.garage_door` at score 92 via the `garbage~garage`
     ratio. typo_fallback now requires ≥50% of distinct query tokens
     to fuzzy-match a doc token; single-token typos like `ligth` are
     unaffected.
  6. `_partial_results_search` removed — the "last resort" fallback
     returned every entity at score 0 with `partial: true`, masking
     real errors. Exceptions now propagate so callers see the cause.
  7. area_only aggregates ALL fuzzy-matched areas (was first-match-wins,
     and from a `set` so non-deterministic). Public-API change: new
     `area_names: list[str]` joins legacy `area_name` (kept as the
     first match for one minor version of compat).
  8. Aliases — entity registry aliases are now folded into the BM25
     corpus (one extra `config/entity_registry/get_entries` round-trip
     after the hidden filter), and area registry aliases are consulted
     in `get_entities_by_area`. Alias-driven matches surface as
     `match_type="alias_match"`.
  9. Hidden-by filter — new `include_hidden: bool = False` parameter
     on `ha_search_entities`. Defaults skip entities where
     `entity_registry.hidden_by` is set (UI-hidden infra entities,
     diagnostic helpers); set True for diagnostics workflows.
 10. Test coverage — new `TestSearchEntitiesSeededAreasIssue1170`
     class exercises the area_filter branches against a multi-domain
     populated area at the scale of the `tests/initial_test_state`
     seed, closing the gap noted in the triage.

Tests
- 5 new unit tests (`tests/src/unit/test_bm25_search.py::TestFuzzySearcherIssue1170`)
- 8 new E2E tests (`tests/src/e2e/tools/test_search_entities.py`)
- 1 new E2E test class with 3 test methods for finding 10
- 2 new regression tests for hidden_by filter in test_search_fallback.py
- Updated test_search_fallback / test_search_pagination to drop
  references to the deleted `_partial_results_search`.

Public API impact
- Adds: `include_hidden` parameter; `area_names` field on area_only;
  `match_type="alias_match"` value.
- Behavior change: `domain_filter` is normalized; hidden entities
  require explicit opt-in; area_only now includes `score`+`match_type`;
  `area_filtered_query` per-result `area_filter` echo dropped;
  `search_type="partial_listing"` no longer returned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Propagate get_states() exceptions in 3 fetch sites instead of
  silently emptying — auth/connection errors now surface rather
  than being masked as "zero matches" with success=true.
- Fix alias_match label: subtract entity_id+friendly_name tokens
  from alias_hit so a query token present in BOTH name and alias
  doesn't mislabel as alias_match.
- Move alias batch-fetch out of get_entities_by_area into
  tools_search.py's area+query branch — get_entities_by_area is
  exposed via server.py, so injecting `_aliases` was leaking an
  internal field through any caller round-tripping the response.
- Tighten alias-fetch except clause to (KeyError, TypeError,
  AttributeError) so unexpected errors propagate while malformed
  payloads still degrade gracefully.
- Sort area_filtered_query iteration order to match area_only.
- Emit `area_names: []` on the empty-area-match branch for
  response-shape symmetry with the populated branch.
- Document case-insensitive domain_filter in the Field description.
- Tighten _strip_separators docstring + _exact_match_search
  docstring (no longer pure — also queries entity registry).
- Soften "preserved for one minor version" comment to "kept for
  backward compatibility" — no removal deadline is committed.
- Add unit test: alias-vs-friendly_name precedence (when query
  token matches both, name match wins, not alias_match).
- Add e2e test: fuzzy-mode hidden filter (the existing test only
  exercised exact_match=True; fuzzy is a separate code path).
- Add e2e test: area_only branch hidden filter +
  include_hidden=true opt-in.
- Add e2e test: total_matches in area_only aggregates across
  all matched areas (locks down finding-7 fix at the pagination
  metadata layer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@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 addresses 10 specific behavioral findings in the ha_search_entities tool, significantly improving search accuracy, API consistency, and the handling of hidden entities. It introduces robust filtering, better alias support, and cleaner fallback mechanisms, while also expanding E2E test coverage to ensure future stability.

Highlights

  • Search Accuracy & Robustness: Implemented concat-token elision and a multi-token coverage gate in typo_fallback to reduce false positives and improve search relevance.
  • Hidden Entity Filtering: Added an include_hidden parameter to search tools to filter out entities marked as hidden in the registry by default.
  • Alias Support: Integrated entity and area registry aliases into the search corpus, enabling more flexible entity discovery.
  • API Cleanup: Standardized result shapes across search branches and removed the deprecated _partial_results_search fallback.
  • Area Search Improvements: Fixed non-deterministic area aggregation and added support for multi-area matching in area_only searches.
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 the 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 counterproductive. 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.

Copy link
Copy Markdown
Contributor

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

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 enhances the entity search system by introducing support for hidden entities and aliases, normalizing domain filters, and improving area-based matching. Key updates include parallelizing registry fetches, implementing a multi-token coverage gate in fuzzy matching to reduce noise, and removing the legacy partial results fallback. Review feedback focuses on ensuring that docstrings for both public tools and internal helpers start with approved action verbs to maintain consistency with project documentation standards.

Comment thread src/ha_mcp/tools/smart_search.py
Comment thread src/ha_mcp/tools/tools_search.py Outdated
Comment thread src/ha_mcp/utils/fuzzy_search.py Outdated
Three docstrings now lead with an approved action verb per
.gemini/styleguide.md and AGENTS.md:

- smart_search.py:smart_entity_search — "Advanced entity search"
  → "Search entities..."
- tools_search.py:_exact_match_search — "Substring search across..."
  → "Search entities by substring..."
- fuzzy_search.py:_strip_separators — "Lowercase ``text``..."
  → "Strip ``.``, ``_``, ``-``..."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add `except ToolError: raise` guard in fuzzy fallback wrapper so
  auth/connection failures from the service layer propagate instead
  of being silently retried via _exact_match_search.
- Lowercase domain_filter at the service-layer boundary too, so
  internal callers of SmartSearchTools.smart_entity_search get the
  same normalization the tool layer applies.
- Log alias-enrichment failures with a structured `alias_enrichment_failed`
  prefix and survivor count, including the previously-silent case
  where send_websocket_message returns success=False.
- Strip task-shaped `(homeassistant-ai#1170 finding N)` / `(closes homeassistant-ai#1166)` parentheticals
  from production-code comments and docstrings; technical why prose stays.
- Drop two paraphrasing comments that restated the code below them.
- Tests: tighten single-token typo assertion to require a `light.*`
  result, lock down `partial_id` precedence over `alias_match` when
  the query token also lives in id/name, assert area_only ordering is
  deterministic across calls, propagation of get_states failures as
  ToolError, plus E2E coverage for area-registry alias resolution and
  separator-elided concat-token queries.
- seeded_bedroom fixture cleanup wrapped in try/finally so a fixture-
  body failure (or pytest.skip) doesn't leak the seed area assignments.
Hidden entities now surface in results with a 20-point score penalty
applied whenever hidden_by is non-None — the option (c) approach from
issue homeassistant-ai#1170 finding 9, in line with the issue's explicit menu of
choices. Visible matches sort above hidden ones at comparable raw
scores; agents that need to see hidden infrastructure entities still
get them, just lower in the list.

- New apply_hidden_penalty(score, hidden_by) helper in fuzzy_search.py;
  wired through every branch that emits a score (BM25, typo_fallback,
  exact_match, area_only, area_filtered_query, domain_listing).
- include_hidden default flips True → callers keep the explicit
  opt-out (False) for visible-only search.
- get_entities_by_area carries _hidden_by through entity dicts so the
  area_filtered_query and area_only branches can apply the penalty
  without a second registry lookup.
- area_only and domain_listing now sort the result list by score so
  visible matches outrank penalised hidden peers within the same
  area/domain.
- Tests rewritten: test_search_excludes_hidden_by_default → includes-with-
  penalty; new TestHiddenScorePenalty class covers helper math, ordering,
  and zero-clamp boundary; existing include_hidden=True opt-in test
  becomes include_hidden=False filter assertion.
Critical:
- BM25 + typo_fallback now gate the threshold on the *raw* score and
  apply the hidden-score penalty only afterwards. Previously a hidden
  entity at raw threshold (60 default, 80 in the area+query searcher)
  was penalised below threshold and silently dropped — partially
  regressing to option (b) for borderline matches and breaking the
  option (c) "still surface, just rank lower" contract.

Important:
- server.py:get_entities_by_area bridge now strips internal
  leading-underscore fields (`_hidden_by`, `_aliases`) via the new
  strip_internal_fields helper so they don't leak to MCP clients.
- All three coerce_bool_param calls in ha_search_entities now sit
  inside the try/except block. A bad string ("maybe") was previously
  raising ValueError that escaped the structured exception handler
  and surfaced as INTERNAL_ERROR.
- _exact_match_search and the domain_listing branch now log
  hidden_filter_unavailable: when the registry/list call returns
  non-success, mirroring the alias_enrichment_failed: pattern. Without
  this an operator can't correlate "diagnostic entity ranking first"
  with a transient WS hiccup.

Suggestions:
- apply_hidden_penalty coerces score to int defensively so a stray
  float caller can't break the result-dict's int score contract.
- New public_fields(d) helper centralises the "strip leading
  underscore" convention for non-mutating call sites; area_only
  branch now uses it.
- Split the dense area_only multi-WHY comment into two focused ones.
- Drop paraphrase first sentence at the area_filtered_query result
  builder; keep the why-comment about the dropped per-result field.

Tests:
- TestHiddenScorePenalty gains test_hidden_borderline_match_still_surfaces
  (locks down the threshold-on-raw-score fix) and
  test_hidden_typo_fallback_penalised (per-branch coverage for the
  typo_fallback path).
- New E2E test_search_area_filtered_query_penalises_hidden_issue_1170
  covers the area_filter+query path's _hidden_by plumbing through
  get_entities_by_area → entities_for_search → BM25.
- New E2E test_search_domain_listing_penalises_hidden_issue_1170
  covers the empty-query+domain_filter path's penalty + sort and
  the include_hidden=False filter.
@kingpanther13
Copy link
Copy Markdown
Member Author

/Gemini review

kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 10, 2026
… Fork-Dev dev106

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

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

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 significantly enhances the search capabilities of the MCP server by implementing alias support for entities and areas, and introducing a score-penalty system for hidden entities. It also fixes a bug where area searches only returned results from a single matched area and improves the typo-fallback logic to reduce noise. Feedback from the review focuses on improving the robustness of exception handling for Home Assistant API calls, ensuring docstrings adhere to the project's style guide, and adding secondary sort keys to guarantee deterministic result ordering.

Comment thread src/ha_mcp/tools/smart_search.py
Comment thread src/ha_mcp/tools/tools_search.py Outdated
Comment thread src/ha_mcp/tools/tools_search.py
Comment thread src/ha_mcp/tools/tools_search.py Outdated
Stress test against the user's real HA caught a domain_filter bug:
"  LIGHT  " was returning 0 results silently because lowercase
normalization didn't strip whitespace first. Now both the tool layer
(ha_search_entities) and the service layer (smart_entity_search)
strip+lowercase, mirroring what was already done case-wise.

Failing E2E test fixes:
- test_search_area_filtered_query_penalises_hidden_issue_1170 was
  timing out for two reasons: missing ha_config_set_helper entity_id
  fallback (could be None when HA returns helper_data.id instead),
  and the no-separator helper name produced a single huge BM25 token
  that the prefix-only query couldn't fuzzy-match. Now uses a
  distinctive token in a space-separated name so BM25 hits at score
  100, and falls back to helper_data.id when entity_id is absent.
- test_search_domain_listing_penalises_hidden_issue_1170 had the same
  entity_id-fallback bug; same fix.
Gemini Code Assist:
- Tool docstring opens with "Search" instead of "Find or list"
  (latter not on the approved-verb list).
- Sort tie-breaker on entity_id wherever the sort key was score-only
  (fuzzy_search BM25 path, _exact_match_search, area_only, and the
  domain_listing scored_entities sort). Without a stable secondary
  key, paginated requests could shift the within-tier order between
  calls — common with the hidden-penalty banding (visible@100,
  hidden@80) and BM25's coarse buckets.

Silent-failure findings:
- Re-raise asyncio.CancelledError when it surfaces as a captured
  exception from gather(return_exceptions=True). Previously the
  `else` branch would log "hidden_filter_unavailable: ... CancelledError"
  and continue, leaving the canceller waiting. Three call sites:
  smart_entity_search, _exact_match_search, and the empty-query
  domain-listing path.
- domain_filter / area_filter strip+lowercase now happens BEFORE the
  at-least-one-set validation. Previously a whitespace-only filter
  ("   ") passed validation truthy then collapsed to "" and fell
  through to a silent zero-result fuzzy search.
- strip_internal_fields now carries an _seen-set cycle guard so a
  future caller feeding it a non-tree structure gets a clean
  short-circuit instead of RecursionError.

Comment cleanup:
- Drop "option-c contract" task-shaped refs from fuzzy_search.py
  comments; the WHY prose stands alone without the triage label.
- Tighten public_fields docstring: drop the parenthetical example
  (one call site at the moment) and document the shallow-copy
  contract so future callers don't trip on shared list values.

Code-reviewer suggestions:
- Drop the dead `query_lower` parameter from _typo_fallback (private
  method, no API to preserve).
- area_filter zero-match echo now uses the canonical (stripped) form
  by virtue of normalisation moved to the tool entry point.

Tests:
- New TestStripInternalFields and TestPublicFields in
  test_util_helpers_internal_strip.py — pin the leak-guard contract
  end to end (recursion, cycles, non-string keys, mutation
  semantics).
- New parametrised E2E test_domain_filter_whitespace_normalized
  (5 padded variants) locks down the strip step that the stress
  test caught was missing.
- New test_domain_filter_whitespace_only_rejected pins the
  validation-order fix.
- TestFuzzySearcherIssue1170::test_hidden_borderline_raw_score_threshold_edge
  constructs a single-token single-doc corpus that lands at exactly
  threshold=100 to lock down the post-gate penalty contract.
- test_search_area_filtered_query_penalises_hidden_issue_1170 escape
  hatch removed: with the distinctive token now hitting BM25 at score
  100, both helpers must surface unconditionally.
Round-4 pr-review-toolkit final pass flagged three coverage gaps that
the prior rounds didn't already close:

1. `TestFuzzySearcherIssue1170::test_score_ties_break_on_entity_id_ascending`
   pins the new `(-score, entity_id)` sort tuple. Without an order
   assertion, a regression that drops the secondary key (or flips its
   direction) silently shifts pagination between calls.

2. `TestFuzzySearcherIssue1170::test_cancelled_error_propagates_from_registry_gather`
   and `TestExactMatchSearchCancelledPropagation::test_cancelled_on_registry_task_propagates`
   lock down the new asyncio.CancelledError re-raise after
   gather(return_exceptions=True). Pre-fix the captured cancellation
   hit the `hidden_filter_unavailable:` log and the function continued
   — the canceller would wait forever.

3. `test_server_bridge_strip.test_get_entities_by_area_bridge_strips_internal_fields`
   exercises the public bridge end-to-end with a mock smart_tools that
   returns a dict carrying `_hidden_by` / `_aliases`. If a future
   refactor deletes the `strip_internal_fields(result)` line in the
   bridge, internal fields would leak to MCP clients with no signal in
   CI — this test catches that.
ImportError: HASmartMCPServer doesn't exist — the actual class is
HomeAssistantSmartMCPServer. Drop the unused monkeypatch fixture
while I'm in there.
smart_tools is a lazy-init property with no setter (server.py:218);
the test was hitting 'object has no setter'. Set the underscored
backing field directly so the property short-circuits to the fake.
kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 10, 2026
… Fork-Dev dev107

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

Claude discovere the following things. Claude is being particularly stupid lately, so these might be nonsense, so confirm they are actually legitimate because there's a good chance that they aren't, but if they are then we should probably fix them.


1. Exact area_id is treated as fuzzy in area_filter

area_filter="bedroom_kids" (a literal area_id from ha_config_list_areas) still partial-matches the parent bedroom because calculate_partial_ratio("bedroom_kids", "bedroom") == 100, which clears the 80 threshold. Result aggregates both areas' entities.

Repro: assign light.bed_light + switch.ac to bedroom, cover.kitchen_window to bedroom_kids, then area_filter=bedroom_kids → returns all 3 entities, area_names: ["Bedroom", "Bedroom Kids"].

If real: short-circuit when area_query_lower in area_registry (skip fuzzy step on exact id hit) in smart_search.py::get_entities_by_area.


2. Single-token sub-word noise in _typo_fallback

The coverage-gate fix from finding 5 is multi-token-only by construction. Single-token queries still surface unrelated entities via SequenceMatcher partial overlap. Original example from the issue: query="lit" → returns todo.shopping_list at score 85.

Repro: ha_search_entities(query="lit", exact_match=False, limit=3)todo.shopping_list@85, light.bed_light@75, light.ceiling_lights@75.

If real: either raise the typo_fallback threshold for single-token queries (~85), or require min query length ≥ 4 for the fallback to fire, in fuzzy_search.py::_typo_fallback.


3. essential_attributes shape asymmetry across branches

Result-shape consistency was the target of finding 3, which closed the score/match_type gap on area_only. But the fuzzy_search branch alone emits an extra essential_attributes dict that the other four branches don't. Either prune from fuzzy or add to the other four.

Repro: compare result_keys of query="bed_light" (exact=true vs false), domain_filter="light", area_filter="bedroom", area_filter="bedroom" + query="light" — only fuzzy has essential_attributes.

If real: decide which way to go, then either drop the field in smart_search.py::smart_entity_search or add it to the other four sites in tools_search.py.


4. Misleading suggestion on VALIDATION_FAILED errors

A limit=0 (or any other validation failure) returns code: VALIDATION_FAILED with suggestion: "Check Home Assistant connection" next to a 3-element suggestions array of the same generic text. None of the suggestions relate to the actual validation problem.

Repro: ha_search_entities(query="light", limit=0).

If real: pass an appropriate suggestions list when constructing the validation error, or drop the suggestion field for validation errors entirely. The error originates from exception_to_structured_error defaults in tools_search.py::ha_search_entities.


5. area_filter + domain_filter no-overlap returns no message

When area_filter matches areas with entities, but domain_filter filters them all out within those areas, the response is total: 0, count: 0 with no message. The empty-area branch emits "No entities found in area: <area>" for shape parity.

Repro: ha_search_entities(area_filter="bedroom", domain_filter="sensor") when bedroom has only lights/switches/covers assigned → returns total: 0 silently.

If real: emit a parallel "No <domain> entities found in area: <area>" message in the populated-but-domain-filtered path in tools_search.py.

1. Exact area_id short-circuits fuzzy aggregation. A query like
   area_filter='bedroom_kids' was partial_ratio-matching its parent
   'bedroom' (score=100, clears the 80 threshold) and aggregating
   sibling areas' entities. Now exact id/name/alias matches suppress
   the fuzzy step entirely; fuzzy only fires when no exact hit
   exists.

2. Single-token typo_fallback min-length gate. The coverage gate
   from finding 5 was multi-token-only by construction: 'lit' (3
   chars) still surfaced every '*_lite*' entity at score 85 via
   partial overlap. Short single-token queries now skip the fallback
   entirely; 4+ chars (typical real typos like 'ligth'→'light') are
   unaffected.

3. Result shape parity. fuzzy_search emitted an 'essential_attributes'
   dict that the other four branches (exact_match, area_only,
   area_filtered_query, domain_listing) never carried — a shape
   asymmetry that issue homeassistant-ai#1170 finding 3 didn't address. Dropped from
   fuzzy_search for consistency; callers needing full state should
   follow up with ha_get_state.

4. Validation errors no longer carry misleading generic suggestions.
   A 'limit=0' input would surface as VALIDATION_FAILED with
   message 'limit must be at least 1, got 0' but suggestions like
   'Check Home Assistant connection' — boilerplate from the generic
   exception handler. ValueError from coerce_*_param is now caught
   separately and surfaced via create_validation_error with the
   helper's own message and no operational suggestions.

5. area_filter+domain_filter zero-overlap now emits a message. When
   areas resolve but the domain_filter wipes out every entity in
   them, the response carries 'No <domain> entities found in area:
   <area>' instead of returning total_matches=0 silently.

Test coverage:
- TestFuzzySearcherIssue1170 gets test_typo_fallback_short_single_token_returns_empty
  and test_typo_fallback_four_char_single_token_still_fires for finding 2.
- New E2E test_exact_area_id_short_circuits_fuzzy_aggregation pins finding 1
  against the two_areas_with_shared_prefix fixture.
- New E2E test_result_shape_consistent_across_branches loops all 5 search_types
  and asserts base-keys-present plus essential_attributes-absent.
- New E2E test_validation_error_carries_no_generic_suggestions hits limit=0
  and asserts the leaker strings are absent.
- New E2E test_area_filter_with_domain_filter_zero_overlap_has_message hits
  area_filter=kitchen + domain_filter=zone (never assigned per-area) and
  asserts the new message field.
kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 10, 2026
… Fork-Dev dev108

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

Implementation Summary

Choices Made:

  • Option (c) over (b) for finding 9 (hidden entities): the issue's investigation listed three approaches; chose the score-penalty (option c) per user direction. Hidden entities still surface but receive a 20-point penalty so visible matches sort above them. include_hidden=False remains as an explicit opt-out for visible-only callers.
  • Action verbs over noun phrases for tool docstrings (Gemini Code Assist flagged "Find or list" → "Search"). Aligned with AGENTS.md approved-verb list.
  • Threshold-on-raw-score in BM25 + typo_fallback: the penalty applies AFTER the threshold gate so a borderline hidden match doesn't get silently dropped (would violate the option-c contract).
  • Exact area_id short-circuits fuzzy aggregation: when an area_filter value is a literal id/name/alias, sibling areas no longer leak in via partial_ratio. Fuzzy aggregation only runs when no exact match exists.
  • Single-token typo_fallback min-length=4: the multi-token coverage gate from finding 5 doesn't help short single-token queries ("lit" matched 186 entities). Added a length gate; typical real typos ("ligth", 5 chars) are unaffected.
  • essential_attributes pruned from fuzzy_search rather than added to the other four branches (shape consistency, less payload; callers fetch full state via ha_get_state if needed).
  • ValueError from coerce_*_param caught separately so VALIDATION_FAILED errors carry only the helper's message, not boilerplate operational suggestions.
  • strip_internal_fields (mutating, recursive, cycle-guarded) + public_fields (shallow, non-mutating) as twin helpers in util_helpers.py. Server.py bridge uses the mutating form; tools_search.py inline comprehensions use the shallow form.

Problems Encountered:

  • Stress test against a real HA instance caught domain_filter=" LIGHT " returning silent 0 — the lowercase normalization didn't strip whitespace first. Fixed with .strip().lower() at both the tool entry and the service layer, then moved BEFORE the at-least-one-set validation so a whitespace-only filter fails validation instead of falling through.
  • Initial E2E test for area_filtered_query + hidden penalty timed out in CI (entity_id fallback missing + name format meant the prefix-only query couldn't fuzzy-match). Rewrote with a distinctive token in a space-separated name so BM25 hits at score 100.
  • server.HomeAssistantSmartMCPServer.smart_tools is a lazy-init property with no setter — bridge test had to set the _smart_tools backing field directly.
  • A second-pass review surfaced 5 additional findings (exact area_id treated as fuzzy, single-token typo_fallback noise, essential_attributes shape asymmetry, misleading VALIDATION_FAILED suggestions, silent empty result on area+domain no-overlap). All five verified as legitimate against live HA and fixed.

@kingpanther13 kingpanther13 marked this pull request as ready for review May 10, 2026 23:52
@kingpanther13 kingpanther13 requested review from a team and Patch76 May 10, 2026 23:52
Copy link
Copy Markdown
Member

@Patch76 Patch76 left a comment

Choose a reason for hiding this comment

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

Triage-comprehensive coverage of #1170 — all 10 findings closed, plus the alias work folding in #1166.

Verified G4/G6 dismissals: client.send_websocket_message (rest_client.py:1101-1129) catches except Exception broadly and returns a {success, error} dict, so HomeAssistantConnectionError / HomeAssistantAPIError / TimeoutError / OSError don't propagate to either alias-enrichment site. The narrow (KeyError, TypeError, AttributeError) catch correctly scopes to malformed-but-success payloads; transport failures hit the explicit non-success else branch that logs alias_enrichment_failed: with the survivor count. Reasoning checks out.

Backwards-compat on Finding 7: tools_search.py:494-503 emits both area_names: list[str] (new) and area_name: str (legacy, first match by sorted area_id), with the soft-deprecation flagged in-line. Not breaking per the styleguide's return-restructuring rule.

Test envelope: +1839 net test lines — one E2E file (test_search_entities.py), two new unit files (test_server_bridge_strip.py, test_util_helpers_internal_strip.py), a BM25 expansion (test_bm25_search.py, +462 net), plus rewrites of test_search_fallback and test_search_pagination. Both new helpers (strip_internal_fields + public_fields) ship with dedicated unit coverage.

@Patch76
Copy link
Copy Markdown
Member

Patch76 commented May 11, 2026

LGTM

@kingpanther13 kingpanther13 merged commit 5076c1d into homeassistant-ai:master May 11, 2026
15 checks passed
@github-actions
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.

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
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.

[FEATURE] Investigate ha_search_entities stress-test findings — triage 6 observed behaviors [FEATURE] Expose Voice Assistant entity aliases

2 participants