Skip to content

fix(addon): enable ingress so the stable add-on shows the Open Web UI / Settings UI - #1486

Merged
kingpanther13 merged 3 commits into
homeassistant-ai:masterfrom
kingpanther13:fix/stable-addon-ingress
May 29, 2026
Merged

fix(addon): enable ingress so the stable add-on shows the Open Web UI / Settings UI#1486
kingpanther13 merged 3 commits into
homeassistant-ai:masterfrom
kingpanther13:fix/stable-addon-ingress

Conversation

@kingpanther13

Copy link
Copy Markdown
Member

What does this PR do?

The stable Home Assistant add-on (homeassistant-addon/) never declared
ingress, so Home Assistant renders no "Open Web UI" button and the web
Settings UI is unreachable on stable — even though stable's start.py already
mounts the settings routes "for the HA ingress proxy" and binds 0.0.0.0
(#960), and #1431 wired beta-feature access into stable's code. The dev add-on
(homeassistant-addon-dev/) has had ingress: true since #960, which is why
the button shows there but not on stable.

Root cause: functional add-on config is not auto-synced between the dev and
stable flavors — the release pipeline only syncs version + changelog — so
ingress had to be added to the stable config.yaml by hand and never was
("the release pipeline handles it" confusion).

Changes

  • homeassistant-addon/config.yaml — add ingress: true /
    ingress_port: 9583 / ingress_stream: true, mirroring the dev add-on's
    proven block. No version bump (release pipeline owns it); no beta
    option/schema changes (beta access stays gated by the web-UI master toggle
    from feat: advanced settings panel + nested beta master toggle (#1164) #1431, per docs/beta.md).
  • tests/addon/test_addon_structure.py — assert the stable add-on declares
    ingress/ingress_port, locking the regression.
  • AGENTS.md — document that functional add-on config must be mirrored
    between flavors by hand, so this class of oversight can't recur.

Type of change

  • 🐛 Bug fix

Testing

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

YAML validated locally (parses; ingress is top-level, no translation entry
needed). Full suite + lint left to CI.

Checklist

  • I have updated documentation if needed

… / Settings UI

The stable Home Assistant add-on (homeassistant-addon/) never declared
ingress, so HA rendered no "Open Web UI" button and the web Settings UI was
unreachable on stable -- even though start.py already mounts the settings
routes for the ingress proxy (homeassistant-ai#960) and homeassistant-ai#1431 wired beta access into stable's
code. Mirror the dev add-on's proven ingress block, add a regression
assertion, and document that functional addon config is not auto-synced
between the dev/stable flavors.

Co-Authored-By: Claude Opus 4.8 (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 resolves an issue where the stable Home Assistant add-on lacked the necessary ingress configuration to display the 'Open Web UI' button. By explicitly adding the required ingress settings to the stable configuration and implementing regression tests, the UI is now accessible. Additionally, documentation has been updated to prevent future configuration drift between the stable and development add-on flavors.

Highlights

  • Ingress Configuration: Enabled ingress, ingress_port, and ingress_stream in the stable Home Assistant add-on configuration to expose the web Settings UI.
  • Regression Testing: Added assertions to the test suite to ensure ingress configuration remains enabled in the stable add-on.
  • Documentation Update: Updated AGENTS.md to clarify that functional add-on configurations must be manually mirrored between stable and dev flavors.
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.

@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 documents the configuration differences between the stable and dev add-on flavors in AGENTS.md, and enables Supervisor ingress in the stable add-on config.yaml to expose the web Settings UI. It also adds corresponding regression tests in test_addon_structure.py. The review feedback recommends extending the test coverage to assert the newly added ingress_stream configuration as well.

Comment thread tests/addon/test_addon_structure.py
kingpanther13 and others added 2 commits May 29, 2026 15:37
Reviewers (Gemini + PR toolkit) noted the regression test locked ingress and
ingress_port but not ingress_stream, which the fix also added. Assert it so a
silent drop of the streaming key is caught too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add a "Tool Settings Web UI" section to the stable add-on DOCS.md (it already
  referenced the web UI / Tool Security Policies tab but never said how to open
  it); stable-adjusted from the dev flavor, dropping dev-only text-field options.
- Note in config.yaml why ingress_stream is required (streamable-HTTP transport).
- Fix AGENTS.md: changelog is synced by the "Copy changelog" step in the
  semantic-release job, not by update-addon-config (which only bumps version).

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

Copy link
Copy Markdown
Member Author

Review feedback addressed (5424c02, 0fdae43)

Thanks for the reviews. Changes since the last push:

  • ingress_stream now asserted in tests/addon/test_addon_structure.py (Gemini + PR-toolkit) — the regression test locks all three ingress keys, not just two.
  • config.yaml comment now explains why ingress_stream is required (streamable-HTTP transport / settings-UI flush behaviour).
  • AGENTS.md corrected: the changelog is synced by the Copy changelog to addon directory step in the semantic-release job, not by update-addon-config (which only bumps the version).
  • Stable DOCS.md gained a "Tool Settings Web UI" section — it already referenced the web UI / Tool Security Policies tab but never told users how to open it (the same dev→stable doc drift this PR closes). Stable-adjusted from the dev flavour; dropped the dev-only disabled_tools/pinned_tools text-field fallback since stable doesn't expose those options.

No runtime change — the fix remains the ingress declaration. Prior commit was fully green in CI.

kingpanther13 added a commit to kingpanther13/ha-mcp-fork that referenced this pull request May 29, 2026
The merge left the dev addon config.yaml version at dev373 while
upstream/master is at dev374, making the PR diff show a backwards
version bump. The version line is release-pipeline-owned; restoring
it to upstream's value zeroes the spurious diff so the PR only changes
enable_mandatory_bps. No stable-version change (stable matches upstream
at 7.6.0).

Audit note (re homeassistant-ai#1486, "addon config isn't auto-synced between flavors"):
verified enable_mandatory_bps is present on BOTH stable and dev addon
config.yaml (options + schema) and translations, is NOT in
BETA_FEATURE_FIELDS (so not beta-gated), and is written unconditionally
in start.py — i.e. the skill_content feature is available on stable, not
accidentally dev-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kingpanther13
kingpanther13 marked this pull request as ready for review May 29, 2026 19:56
@kingpanther13
kingpanther13 requested review from a team and sergeykad May 29, 2026 19:56

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

The ingress fix is correct and verified against the code: the block exactly mirrors the dev add-on (ingress: true / ingress_port: 9583 / ingress_stream: true), start.py binds 0.0.0.0:9583 and mounts the settings routes for the ingress proxy, so the Settings UI is genuinely reachable on stable now. ingress_port is consistent with the existing ports: 9583/tcp, the regression test targets the stable config.yaml and locks all three keys, and there's no exposure regression — ingress is gated by HA's authenticated Supervisor proxy and beta features stay default-off behind the #1431 master toggle.

One minor doc accuracy nit, your call on whether to bundle it here: the new Mandatory tools line in DOCS.md lists 4 tools, but MANDATORY_TOOLS in settings_ui.py:88 actually has 6 — it also includes ha_get_skill_guide and ha_manage_backup. Adding those two would keep the list matching the code.

Approving — the fix itself is solid and ready.

@kingpanther13
kingpanther13 merged commit 85fa575 into homeassistant-ai:master May 29, 2026
12 checks passed
kingpanther13 added a commit that referenced this pull request May 30, 2026
…e checker warnings with embedded skills responses (#1182) (#1448)

* feat(internal): add skill_loader utility with path-traversal guards

Shared helper for resolving (skill, file) pairs from the bundled
skills-vendor directory. Mirrors the symlink + path-traversal guards
in ha_get_skill_guide's file read path, but silently skips bad files
instead of raising so write tools can use it for response embedding
without failing the operation.

Refs #1182

* refactor(internal): server._get_skills_dir delegates to skill_loader

Single source of truth for the skills-vendor path lookup. The new
delegate matches the existing exists()-only check exactly — no
behaviour change for ha_get_skill_guide or any other caller — and
gives the write-tool include_skill parameter the same path resolver
without holding a server reference.

Also simplifies skill_loader._skills_dir_at to a bare existence check
so the two paths stay byte-equivalent.

Refs #1182

* refactor: best_practice_checker returns dataclass; warning text names 3 access routes

check_automation_config / check_script_config now return
BestPracticeCheckResult — a list[str] subclass that also exposes a
.referenced_files set of skill file paths each warning points to.
Existing call sites (and tests) that treat the return as a plain list
keep working unchanged; new callers use .referenced_files to fetch
file bodies via skill_loader and embed them in responses.

Each warning's ' See ...' suffix now names all three skill-access
routes when skills are enabled:

  See skill://... | call ha_get_skill_guide(skill=..., file=...) |
  or pass include_skill=True on this tool to receive the file in the
  next response automatically

When skill_prefix=None (skills feature off server-wide), the suffix
is suppressed entirely — matches historical behaviour because none of
the three routes resolve when skills are off.

Internal sweep: every warnings.append(... + _ref(...)) site replaced
with _emit(warnings, ..., file_ref) so the referenced_files set stays
in sync with the warning strings.

Refs #1182

* feat: include_skill=True default on ha_config_set_automation

Adds include_skill: bool = True parameter. When True (default), the
response carries skill_content: {path: body} populated from the
canonical mapping (automation-patterns.md + template-guidelines.md).

Independent of include_skill, best-practice warnings auto-populate
the same skill_content field with whichever reference files those
warnings cited — so the LLM always gets the relevant guidance inline
on the first wrong attempt, no follow-up call needed.

bp_warnings storage typed as BestPracticeCheckResult so the
referenced_files set is accessible to the response builder.

Refs #1182

* refactor(internal): hoist build_skill_content to util_helpers

Shared helper now lives in util_helpers.build_skill_content so each of
the six write tools getting include_skill (automation, script, scene,
helper, dashboard, yaml) imports the same implementation. Per-tool
canonical mapping stays local to each module.

Refs #1182

* feat: include_skill=True default on ha_config_set_script

Mirrors the ha_config_set_automation pattern: response carries
skill_content with automation-patterns.md + template-guidelines.md by
default, auto-embeds referenced files on BP-checker warnings (works in
both python_transform and full-replace modes).

Refs #1182

* feat: include_skill=True default on ha_config_set_scene

No scene-specific reference file exists. Returns the top-level
home-assistant-best-practices SKILL.md doc by default, which links
out to the relevant references for action/condition design (scenes
share action syntax with automations/scripts).

Refs #1182

* feat: include_skill=True default on ha_config_set_helper

Adds include_skill: bool = True parameter. _attach_helper_skill()
post-processes each of the 4 success return sites (simple-create,
simple-update, config-store-update, and the flow-helper branch) to
attach skill_content with references/helper-selection.md — the
decision matrix for picking the right helper type (input_*, counter,
timer, template, group, utility_meter, etc.).

Refs #1182

* feat: include_skill=True default on ha_config_set_dashboard

Returns dashboard-guide.md + dashboard-cards.md by default — layout
patterns and card-type taxonomy. Single success-return site wraps via
_attach_dashboard_skill helper.

Refs #1182

* feat: include_skill=True default on ha_config_set_yaml

Returns template-guidelines.md by default. YAML packages frequently
include template sensors, command_line entities, and mqtt templates —
exactly where template misuse causes the most subtle bugs.

Refs #1182

* test: cover build_skill_content + per-tool canonical mappings

Unit tests pin the shared assembly contract: canonical files attached
when include_skill=True, suppressed when False; BP-warning referenced
files always attach; canonical and referenced dedupe; missing
canonical files silently skipped; missing skills-vendor degrades to
no-op. Plus a pin per tool's canonical mapping so any future change
to one of the six write tools' skill assignment is a deliberate edit
caught by these assertions.

Refs #1182

* feat: section-slice reactive auto-embed via #anchor (issue #1182 Q3)

Best-practice warnings already point at specific markdown anchors
(e.g. automation-patterns.md#native-conditions). Previously the
auto-embed path shipped the whole 20 KB reference file. Now it ships
just the matching section — typically 1-5 KB. Reactive content cost
drops 5-15x with no information loss.

Implementation:

- skill_loader gets extract_section(body, anchor) — GH-style slugifier,
  fence-aware (a ``# yaml-comment`` inside ```yaml ... ``` no longer
  false-closes the surrounding section).
- resolve_skill_files accepts "path#anchor" entries; reads each file
  at most once even when multiple sections are requested from it.
- best_practice_checker._emit preserves the anchor in referenced_files
  instead of stripping to bare path.
- util_helpers.build_skill_content dedupes bare-vs-anchored for the
  same file (full file in canonical supersedes a sliced section ref).

Tests cover: anchor extraction, fence handling, ifthen-style slash
slugs, missing anchor silently skipped, file-read dedup across multiple
sections, bare-vs-anchored supersession at the right granularity.

Refs #1182

* docs(internal): trim ha_config_set_automation docstring (-6.5 KB)

Removed schema enumerations (REQUIRED FIELDS / OPTIONAL CONFIG FIELDS,
TRIGGER/CONDITION/ACTION TYPES, blueprint vs regular type breakdown),
extended worked examples (motion light, blueprint create/update), and
the embedded PREFER NATIVE OVER TEMPLATES cheat-sheet. All of that
now ships in the response under skill_content via automation-patterns.md
+ template-guidelines.md by default; the reactive checker additionally
embeds the relevant section on warnings.

Kept the action-verb summary, the when-NOT-to-use routing
(scene/helper alternatives), the two-mode contract (config vs
python_transform with config_hash), and the pointer to where the
templating guidance lives now (skill_content / best_practice_warnings).

7,453 → 979 chars (-6,474). Catalog cost down by ~1.5K tokens for
this single tool. The guidance is not lost — it arrives in the
response on every write call (and just the relevant section on
warnings) instead of riding along in the catalog forever.

Refs #1182

* docs(internal): trim ha_config_set_script docstring (-3.9 KB)

Mirrors the ha_config_set_automation trim. Removed schema field lists,
6 extended worked examples (delay, blink, parameters, blueprint
create/update), and the embedded PREFER NATIVE OVER TEMPLATES
cheat-sheet. All of that arrives in the response via skill_content
(automation-patterns.md + template-guidelines.md by default; relevant
section on BP warnings).

Kept the action-verb summary, the when-NOT-to-use routing (use
ha_config_set_automation for trigger-based work), the two-mode
contract, the sequence-vs-use_blueprint requirement, and the pointer
to where the templating guidance lives now.

4,762 → 869 chars (-3,893).

Refs #1182

* docs(internal): trim ha_config_set_dashboard docstring (-3.0 KB)

Removed python_transform worked examples, MODERN BEST PRACTICES list,
DISCOVERING ENTITY IDs preamble, DOCUMENTATION cross-refs, and 4
verbose dashboard config examples (empty, sections, strategy, update).
All of that ships via skill_content (dashboard-guide.md +
dashboard-cards.md by default).

Kept the two-mode contract, the index-shift caveat for chained
python_transforms, the strategy-vs-custom note, the url_path naming
rules, and pointers to entity-discovery tools.

4,058 → 1,047 chars (-3,011).

Refs #1182

* docs(internal): trim ha_config_set_helper docstring (-2.0 KB)

Removed the full SIMPLE/FLOW type enumerations (12 simple + 15 flow +
config_subentry), the verbose Behavior notes preamble, and 4 worked
examples (template sensor, group, tod, config_subentry). The
helper-type decision matrix and worked examples now ship in
skill_content via helper-selection.md by default.

Kept the param-required-by-mode table, the action= disambiguation
contract, the schema-discovery-on-first-error pattern, and the
update-field-preservation note (all behavior contracts the LLM needs
at call time, not reference docs).

3,282 → 1,225 chars (-2,057).

Refs #1182

* docs(internal): trim ha_config_get_automation docstring (-0.3 KB)

Examples and "use ha_get_skill_guide" pointer removed — single required
parameter makes the call self-evident, and the matching set_automation
call now ships skill_content automatically. Kept the return-shape
contract (config_hash + automation_id resolution) the LLM needs at
call time.

708 → 425 chars (-283).

Refs #1182

* docs(internal): trim ha_config_get_script docstring (-0.4 KB)

Examples and behavioral-parity-with-automations note removed; the
prefix-strip and bare-key contract is now stated once, concisely.

Refs #1182

* docs(internal): trim ha_config_get_scene + ha_config_set_scene docstrings

Both shorted: examples removed, schema-shape reduced to one-liner,
ha_get_skill_guide pointers dropped (skill_content ships in set_scene
responses by default).

get_scene: 455 → 220 (-235); set_scene: 1,322 → 850 (-472). Total -707.

Refs #1182

* docs(internal): trim ha_config_list_helpers docstring (-0.8 KB)

Removed the per-helper-type one-line descriptions (the same
information is already enumerated by the Literal[...] on
helper_type, visible to the LLM as the JSON-schema enum), the
examples, and the skill_guide pointer. Kept the
storage-vs-YAML scope note and the simple-vs-flow routing
(use ha_search_entities for flow-based types).

1,318 → 538 chars (-780).

Refs #1182

* docs(internal): trim ha_config_get_dashboard docstring (-0.9 KB)

Examples (8 of them) and the search-workflow walk-through removed.
Kept the three-mode contract (list/search/get) and the config_hash
return-shape distinction (present in get/search, absent in list).

1,978 → 1,071 chars (-907).

Refs #1182

* docs(internal): trim ha_call_service docstring (-0.8 KB)

Removed 4 worked examples, the per-parameter Markdown bullet list
(parameter descriptions are already on the Annotated[Field()] entries
in the signature), and the skill_guide pointer. Kept the
domain.service pattern, the omitted-entity_id targeting note, the
return_response / wait contract, and the discovery-tool pointers.

1,593 → 711 chars (-882).

Refs #1182

* docs(internal): trim ha_config_set_yaml docstring (-0.3 KB)

Collapsed the per-tool routing bullet list into one sentence (the LLM
already knows the alternatives from the catalog). Replaced the
skill_guide pointer with a note that template-guidelines.md ships in
this response by default. Kept the LAST RESORT warning, the YAML-only
scope (allowed keys), the post_action reload-vs-restart note, and the
comment/tag preservation guarantee.

1,240 → 1,036 chars (-204).

Refs #1182

* fix: attach skill_content on ha_config_set_dashboard python_transform path

The python_transform branch built and returned transform_result without
calling _attach_dashboard_skill, so include_skill=True was a silent no-op
on the recommended edit mode. Only the create/update branch wrapped.

Refs #1182

* fix: attach skill_content on ha_config_set_helper config_subentry path

The config_subentry branch returned set_config_subentry's response
directly without calling _attach_helper_skill, so include_skill=True
was a silent no-op on this fifth return site. The other four return
sites (flow, simple-create, simple-update, config-update) already
wrap.

Refs #1182

* fix: attach skill_content on ha_config_set_scene python_transform path

The python_transform branch built and returned response without ever
calling build_skill_content, so include_skill=True was a silent no-op
when editing an existing scene. Only the config-replacement branch
wrapped.

Refs #1182

* docs(internal): correct scene docstring + module comment (D1, D3)

Three sites claimed scenes 'share action syntax with automations/scripts'
or that SKILL.md links out to 'action/condition design' references.
Factually wrong: scenes are pure state snapshots — only an entities
dict, never triggers/conditions/actions. The validator at
_validate_scene_config rejects list shape with exactly this distinction.

Rewrote the module comment and include_skill Field description to
state honestly what SKILL.md actually covers (entity-naming,
safe-refactoring, helper-vs-template trade-offs) and why it's still
relevant for scene authoring.

Re-added the entities-dict shape with a concrete example to the
public docstring — the prior trim dropped the only example, and no
shipped skill file carries scene examples, so the LLM had no
on-call-site reference for the first-write payload.

Refs #1182

* docs(internal): drop stale helper count from ha_config_set_yaml (D2)

Said '27 helper types' but Literal on set_helper.helper_type lists 28
(includes config_subentry, which the prior count missed). Switched to
an enumeration of representative types instead of a count so future
helper additions don't introduce drift again.

Refs #1182

* docs(internal): align checker docstrings with skill_prefix=None impl (D4)

Module docstring and check_automation_config arg doc both promised
that when skill_prefix=None, the URI route would be omitted but the
ha_get_skill_guide tool route and the include_skill parameter route
would still be mentioned. The implementation (_three_route_suffix
returns '' when skill_prefix falsy) suppresses the entire suffix —
correctly, because skill_prefix=None signals skills are disabled
server-wide, in which case none of the three routes can resolve.

Updated both docstring sites to match the implementation rather than
the other way around (the implementation is the right behaviour; the
docstrings were aspirational).

Refs #1182

* fix(internal): preserve referenced_files on copy/deepcopy (L1)

BestPracticeCheckResult.__init__ resets referenced_files to an empty
set, so the default list-subclass copy protocol (which re-enters
__init__ with self as items) silently dropped the auto-embed payload.
Added __copy__ and __deepcopy__ overrides that explicitly carry the
set across the copy.

No current caller uses copy/deepcopy on the result, so this is
latent; fixing now avoids a debugging trap for any future consumer
(e.g. a Transform layer that wants to forward the result).

Refs #1182

* fix: slugify both sides in extract_section + cover edge cases (L2)

The slugifier was applied to the heading from the file but NOT to the
caller-provided anchor, so an asymmetric comparison silently missed on
trailing whitespace, double-hash typos, mixed-case anchors, etc. All
current _emit() sites pre-slugify, so the bug is latent — but the
asymmetry was a footgun for any future change touching anchor strings.

Now both sides are slugified before comparison.

Tests added:
- trailing/leading whitespace tolerance
- mixed-case tolerance
- ## / # typo absorption
- last-heading section runs to EOF
- first-match wins on heading-slug collision

Refs #1182

* fix: surface skills-vendor-missing as a top-level warning (L3)

Previously: include_skill=True on any of the 6 write tools silently
omitted the skill_content field when the bundled skills-vendor
submodule wasn't initialised. Asymmetric vs the read-side
ha_get_skill_guide tool (server.py:200-208), which surfaces a
structured degraded:True payload for the same condition. Operators
on Docker / source installs who skipped --recurse-submodules got a
silently degraded server.

New shared helper util_helpers.attach_skill_content: attaches
skill_content as before AND appends a top-level warnings[] entry when
the caller requested skill content (include_skill=True OR
referenced_files non-empty) AND the vendor is missing.

Swept all six write tools to delegate through this helper:
- automation/script/scene/yaml: inline `if skill_content: result[...]=`
  blocks replaced with attach_skill_content() calls.
- helpers / dashboards: the per-tool _attach_*_skill wrappers now
  delegate to the shared helper (same single point for the
  degraded-warning behaviour).

User-opted-out path (include_skill=False, no BP-warnings) stays
silent — only requests-that-can't-be-fulfilled surface the warning.

Refs #1182

* fix: escalate skill_loader log levels (O1)

Every failure mode in _read_file_safely logged at DEBUG, which is
below the default LOG_LEVEL=INFO — symlink rejects, path-traversal
rejects, and missing files were all invisible in production. Combined
with the write tools' silent-degrade contract, the operator had zero
feedback on these conditions.

Now WARNING for:
- symlink rejection (security event)
- path-traversal rejection (security event)
- missing file / not-regular-file (caller bug or submodule drift)
- OS errors during resolve/read

Plus: missing anchor in extract_section was silently dropped with no
log at all — now logs WARNING in resolve_skill_files since a missing
anchor means a _emit() site typo or vendor submodule heading rename,
both of which are real bugs.

Refs #1182

* test: structural attach coverage + real-skill anchor resolution (T1, T2, T3)

Two parametrized test classes that would have caught the bugs fixed
earlier in this PR (the three missing attach_skill_content calls on
python_transform / config_subentry branches) without needing the full
fastmcp stack:

* TestWriteToolAttachCoverage — AST-scans each of the six write tools,
  counts success-return paths, and asserts an attach-helper call exists
  for each. Pins the wrap-against-every-return-site contract structurally.

* TestEveryEmittedAnchorResolves — extracts every literal anchor passed
  to _emit() in best_practice_checker, plus every per-tool canonical
  file mapping, and resolves each against the real bundled
  skills-vendor submodule. Catches submodule heading renames and
  _emit() typos that would otherwise produce silent empty
  skill_content with no test signal.

Real-skill tests skip cleanly when the vendor submodule isn't
initialised so a fresh clone doesn't fail collection.

Refs #1182

* style: ruff SIM114 + PERF401 cleanup in skill_content_wiring test

Combined the dual isinstance branches in _count_attach_calls into a
single or-chained condition, and switched the inner-loop append
in _canonical_files_mappings to list.extend with a generator.
Behaviour-equivalent.

Refs #1182

* docs(internal): restore full ha_call_service docstring from upstream

ha_call_service has no skill_content, no best-practice checker, and
no include_skill parameter — the original trim (commit 5694b3b9)
plus the merge-conflict resolution dropped Basic Usage examples that
nothing else replaces for this tool. Restored the full upstream
post-#1447 docstring (Basic Usage + Key behavior + skill_guide
pointer + Common patterns trailer) verbatim.

Comment-analyzer agent flagged this as finding #4 earlier and I
wrongly demoted it to LP4. Fixing now: this trim was asymmetric
with the rest of the PR and the tool's high call frequency makes
losing the docstring guidance especially costly.

Refs #1182

* docs(internal): restore read-only tool docstrings — no skill_content path

Five GET/LIST tools were trimmed under the same trim philosophy as the
write tools, but they don't ship skill_content (only write tools do)
and they're not best-practice-checker-gated either. The LLM has no
alternate channel for the dropped guidance on these tools.

Reverted the docstrings to their pre-trim verbatim shape:
- ha_config_get_automation (was commit da9bc359)
- ha_config_get_script       (was commit b23b23e0)
- ha_config_get_scene        (was part of commit 46d49aa1; set_scene trim kept)
- ha_config_list_helpers     (was commit 639f8f0d) + flow-helper routing kept
- ha_config_get_dashboard    (was commit 7163e0c8)

Addresses inline review comments on #1448 noting these trims were
inappropriate for read-only tools that have no skill-fallback path.

Refs #1182

* docs(internal): restore ha_config_set_automation docstring (move PREFER NATIVE to top)

Addresses inline review on PR #1448. Per the reviewer's notes:
- PREFER NATIVE OVER TEMPLATES block restored AND moved to the TOP of
  the docstring (was buried; a hallucinating LLM that's ignoring the
  skill content needs this front-and-centre to avoid reaching for
  Jinja first).
- AUTOMATION TYPES + REQUIRED FIELDS (regular vs blueprint) restored —
  blueprint workflow has zero coverage in skill files.
- OPTIONAL CONFIG FIELDS list restored — category / initial_state /
  variables aren't in skill content.
- BASIC EXAMPLES + BLUEPRINT EXAMPLES restored — full worked configs
  for time-trigger, motion-light, update, blueprint create/update.
- TRIGGER/CONDITION/ACTION TYPES enumeration restored — fills the
  zone-trigger / template-trigger / device-condition / parallel /
  delay gaps in automation-patterns.md.
- TROUBLESHOOTING restored with the reviewer's requested clarification:
  ha_eval_template is for "IF you must use Jinja and have no native
  alternative" — frames it as a fallback, not a default.

Kept the skill_content delivery note (auto-embed on warnings + canonical
files via include_skill) since that's the new mechanism this PR adds.

Refs #1182

* docs(internal): restore ha_config_set_script docstring (PREFER NATIVE at top, fields: + blueprints back)

Per inline review: PREFER NATIVE OVER TEMPLATES moved to the top
(reviewer marked line 434 'This absolutely needs to be left in, and
needs to be at the very top.'). Restored sections:

- SCRIPTS vs AUTOMATIONS routing kept high.
- python_transform examples (delete/append/replace) restored — no
  skill file documents the syntax.
- Required + optional config fields restored — script 'fields:' has
  zero skill coverage; its purpose (caller-supplied input with
  selector schema) is script-only.
- Worked examples for delay, blink, parameterised backup, update,
  blueprint create/update restored — blueprint workflow has zero
  coverage in skill files.

Refs #1182

* docs(internal): restore ha_config_set_scene docstring

Per inline review (line 520): make sure worked example is in the
docstring since SKILL.md (the only file set_scene ships) has no
scene example. Restored the upstream WHEN TO USE / WHEN NOT TO USE /
SCENE SHAPE / EXAMPLE structure verbatim. Kept the skill_content
delivery note for the include_skill mechanism.

Refs #1182

* docs(internal): restore ha_config_set_helper docstring

Per inline review (line 2397): make sure most important + relevant
info isn't excluded. Restored full upstream docstring covering:

- SIMPLE vs FLOW vs CONFIG_SUBENTRY dispatch model with full type
  enumerations (helper-selection.md doesn't have a clean SIMPLE/FLOW
  table; 'trend' helper is in the FLOW list but missing from the
  skill file; subentry workflow has zero skill coverage).
- All four required-params-by-mode rules.
- Behavior notes (UPDATE preservation, action= disambiguation,
  silent-ignore + data_schema discovery, menu_options for menu-rooted
  types).
- Worked tool-call examples for template / group / tod /
  config_subentry — these are first-call-payload-non-obvious shapes
  the LLM needs explicitly. The skill describes the menu flow in
  prose only; the tool-call shape was gone with no replacement.

Refs #1182

* docs(internal): restore ha_config_set_dashboard docstring + clarify yaml-mode distinction

Per inline review (lines 932, 1010, 559, 594, 576, 574):
- python_transform examples (5 patterns: icon update, append, del,
  pattern loop, multi-op chain) restored — no skill file documents
  python_transform syntax for dashboards.
- Strategy-based dashboard example + 'Take Control' caveat restored
  — strategy dashboards have zero skill coverage.
- MODERN DASHBOARD BEST PRACTICES list restored. Dropped the stale
  '2024+' qualifier (it's 2026 now; this section is the current
  guidance, not a recent-add note). Reviewer flagged this should
  also be kept up to date in the skills repo separately.
- title/icon/require_admin/show_in_sidebar update-alongside-config
  behavior note restored.
- DISCOVERING ENTITY IDs section restored verbatim.
- All 4 example shapes restored (empty / sections-tile / strategy /
  update).
- NEW: STORAGE-MODE vs YAML-MODE DASHBOARDS section disambiguates
  the two YAML cases per reviewer's clarification at line 594 —
  dedicated .yaml file referenced from configuration.yaml, vs
  directly inlined under configuration.yaml's lovelace: key. Tool
  covers neither; pointer to ha_config_set_yaml for the latter.

Refs #1182

* docs(internal): restore ha_config_set_yaml docstring (intact + skill_content note)

Per inline review (line 182): leave docstring intact except the
skill-access blurb. Restored full upstream LAST RESORT structure +
routing bullets + intended-use scope + post_action / comment-tag /
replace semantics. Helper-count corrected to 28 (was 27). Added the
include_skill / skill_content delivery note for the new mechanism;
kept the ha_get_skill_guide pointer for deeper guidance.

Refs #1182

* docs(internal): minor wording revert to upstream verbatim on 3 docstrings

Trimmed wording drift introduced during the partial restorations
back to upstream-verbatim:

- set_automation, set_script: 'before writing' (was 'BEFORE writing'
  — cosmetic case change reverted).
- set_automation, set_script: integrated the skill_content auto-embed
  note as an additional sentence after the original 'will surface
  anything in a logic position' line rather than replacing that line —
  keeps the original wording intact.
- set_script: restored 'Creates a new script or updates...' paragraph
  + 'fields:' wording + 'Create script with parameters:' header to
  upstream. SCRIPTS vs AUTOMATIONS moved back to its original
  position after Optional fields.
- set_scene: SCENE SHAPE sentence reverted to upstream order
  ('Automations use a list of actions; scenes capture a snapshot
  of states as a dict').

Set_automation's PREFER NATIVE placement at the top, the
ha_eval_template clarification, and the skill_content delivery
notes are kept — those are the user-requested additions.

Refs #1182

* feat: hide include_skill param from schema, teach opt-out via response hint

BAT-confirmed regression: every LLM (Claude/GPT/Gemini variants) saw the
default-on include_skill bool in the tool catalog and reflexively set it
to False, defeating the proactive-skill-delivery design from PR #1448.

Fix via FastMCP `exclude_args=["include_skill"]` on all six write-tool
decorators. The JSON schema published to clients no longer lists the
parameter, so LLMs can't pre-emptively disable what they can't see. The
runtime still accepts include_skill=False when passed explicitly
(Pydantic's default `additionalProperties: true`), so the opt-out path
remains functional for callers who know about it.

How the LLM learns about the (now-hidden) opt-out: when skill_content
is actually delivered, `attach_skill_content` injects a sibling
`skill_content_hint` field that names the parameter and tells the LLM
to pass `include_skill=false` on subsequent calls if the content has
already been received. The hint only ships alongside delivered content,
so it can't be acted on before the model has seen what it's opting out
of — eliminating the BAT-observed reflex-disable failure mode.

Best-practice-checker route suffix becomes 2-route (skill:// URI +
ha_get_skill_guide) — the previous 3rd route, "pass include_skill=True",
is dropped because it pointed at a parameter no longer in the catalog.
The auto-embed on warnings is unchanged; only the advertised routes are
reduced.

Tool docstrings drop the now-misleading "(see include_skill)" mentions
on all six write tools — the param is hidden, so referring the LLM to
it from the docstring was self-contradictory.

Renamed `_three_route_suffix` → `_skill_route_suffix` and updated the
module-level docs to describe the 2-route shape.

Tests:
- New `test_include_skill_is_hidden_from_tool_catalog` in
  test_skill_content_wiring.py — AST-pins exclude_args=["include_skill"]
  on every write tool's @tool / @mcp.tool decorator.
- `test_happy_path_attaches_skill_content` now asserts the new
  `skill_content_hint` ships with delivered content.
- `test_nothing_requested_is_silent` asserts the hint is absent when
  no content is delivered.
- `TestThreeRouteWarningSuffix` updated: the include_skill-route test
  is replaced with `test_warning_does_not_mention_include_skill_param`
  (positive assertion that the hidden param is never named in warnings).

Closes the BAT regression on PR #1448. Feature flag for the design is
still issue #1182.

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

* fix: hoist skill_content_hint to top of response, imperative wording

BAT regression on the hidden-include_skill change: even Opus needed
five tries to find and act on the opt-out hint when it trailed the
~25KB skill_content body; Sonnet/Haiku never found it. Cause is
two-part — placement (LLMs process top-down and the hint was the last
key after a giant payload) and voice (conditional "if ... then" reads
as advisory, not actionable).

Fix: reorder the response so skill_content_hint is the FIRST key and
skill_content is the LAST, with the operation result fields (success,
data, entity_id) sandwiched between. Reword the hint to imperative
voice: "Pass `include_skill=false` on subsequent calls to this tool
in this session to skip this content."

Mutation is in place via response.clear() + re-insertion because the
write-tool callers pass the dict by reference and expect their handle
to keep pointing at the same response object.

Test: new test_hint_appears_first_and_content_last in
test_build_skill_content.py pins the key ordering contract and
asserts the other response fields are preserved between hint and
content. Existing test_happy_path_attaches_skill_content already
asserts the hint value via the _SKILL_CONTENT_OPTOUT_HINT constant
so the wording change is picked up there automatically.

If smaller models still miss the hint after this, the fallback is to
re-expose the param under an obscure name (visible-but-undescribed).

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

* feat: re-expose include_skill as opaque attach_skill_payload param

BAT regression on the hidden-param + top-of-response-hint approach:
no model ever opted out when the hint was visible at the top of the
response, even after multiple calls in a session. Hidden visibility
was working, but the LLM could not act on the hint because it
couldn't see a callable param in the schema to set.

Switching to the visible-but-opaque strategy:

1. Drop ``exclude_args=["attach_skill_payload"]`` from every write-tool
   decorator so the param appears in the published MCP schema again.
2. Rename ``include_skill`` → ``attach_skill_payload`` everywhere. The
   new name was chosen so the schema shows a default-True boolean with
   no obvious "this is the skill toggle, flip me to disable" semantic —
   "attach" reads as an internal data-shaping flag rather than a
   user-facing feature, "payload" suggests internal/advanced framing.
3. Strip the Pydantic Field description on the param across all 6
   write tools (Field(default=True) only, no description=). The schema
   now publishes only the bare type + default, with no prose telling
   the model what the param does.
4. Remove the parameter's mention from every tool docstring (already
   done previously when the (see include_skill) parentheticals were
   dropped; the docstrings now describe only the by-default behaviour
   of skill_content, never the param).

The opt-out hint (still imperative, still first key in response)
becomes the ONLY surface that names the param and explains when to
set it. A model that reflexively toggles unexplained schema
parameters has nothing semantic to pull on; a model that needs to
opt out reads the hint and now has a callable name plus a usage rule
in the same sentence.

Tests:
- test_attach_skill_payload_is_visible_in_tool_catalog (NEW) — asserts
  the decorator does NOT carry exclude_args=["attach_skill_payload"],
  pinning the visibility decision so a future PR can't quietly
  re-hide the param and revert to the broken state.
- test_attach_skill_payload_has_no_field_description (NEW) — asserts
  the Field on the param carries no description kwarg, pinning the
  opacity decision so a well-meaning future PR can't re-add a
  description that defeats the opacity.
- All existing tests updated for the new param name (find/replace
  across test_build_skill_content.py, test_best_practice_checker.py,
  test_skill_content_wiring.py).

Module docstrings (best_practice_checker.py, skill_loader.py,
server.py) updated to reference the new name and the visible-opaque
design rationale.

If this fallback also fails BAT (small models don't pick up the hint),
the next option is to leave behaviour alone but also embed the
opt-out instruction inline at the START of the skill_content body
itself so the LLM cannot avoid reading it.

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

* fix: rename attach_skill_payload to opaque enabled (BAT round 4)

BAT round 3: Opus could still see ``attach_skill_payload`` had skill
semantics in the name and reflex-disabled every try. The word "skill"
in the param name was the giveaway — even with no description, the
name itself told the model what it controlled.

Renamed to ``enabled`` — completely generic, no domain semantic. A
model scanning the schema sees a default-True boolean named "enabled"
on a write tool and the natural read is "this gates whether the tool
runs at all, don't touch it" — gatekeeper framing prevents the
reflex-disable. The hint shipped inside the response when content is
delivered clarifies that ``enabled=false`` only skips the
skill_content payload, not the write itself.

Hint text updated: "Pass ``enabled=false`` on subsequent calls to
this tool in this session to skip this content." Still imperative,
still first key in the response.

Mechanical rename across 14 files (6 write tools + 2 internal
helpers + util_helpers + best_practice_checker + skill_loader + server
+ 4 test files). No collisions with existing ``enabled`` usages
(other occurrences are docstring text about automation enabled state,
unrelated).

If THIS still BATs poorly:
- Opus picks up that "enabled=false" gates the write itself and stops
  using it → revisit with prior_skill_delivery (inverted polarity)
  reframe.
- Models still don't opt out → fallback is to inline the directive at
  the top of the skill_content body itself (user's earlier ask),
  forcing the LLM to read it as part of the content.

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

* fix: rename enabled to MandatoryBPS, make REQUIRED (BAT round 5)

BAT round 4: ``enabled`` worked as opacity for some models but Haiku
just OMITTED the param entirely on every call (no default fallback
to opt out, but also no signal it should opt in to skip content).

Two changes this round:

1. Rename ``enabled`` → ``MandatoryBPS`` (Mandatory Best-Practice
   Skills). The capital-cased ``Mandatory`` prefix nudges models away
   from reflex-disabling — flipping a thing labelled "mandatory" to
   false reads as actively breaking something. Capital-cased to make
   the wordplay legible (Python convention is snake_case but the
   user's directive was explicit on the name).

2. Make the param REQUIRED at the schema level. Drop ``default=True``
   from the Field, drop ``= True`` from the Python signature, and put
   a ``*,`` kwarg-only separator before it so Python's "non-default
   follows default" syntax rule doesn't fire. The MCP runtime always
   uses kwargs so no caller is broken.

   This forces the model to pass either true or false on every call —
   the previous "Haiku omits the param" failure mode is now impossible
   because FastMCP's Pydantic validator rejects the call without it.
   The default semantic remains "pass true if you have no opinion";
   the response-side hint teaches when to pass false.

Hint text auto-updated via the constant rename: "Pass
``MandatoryBPS=false`` on subsequent calls to this tool in this
session to skip this content." Still imperative, still first key in
the response.

New structural test test_MandatoryBPS_is_required pins:
  * Field has no ``default`` / ``default_factory`` kwarg
  * Python signature has no default for MandatoryBPS (either no entry
    in kw_defaults for kwarg-only params, or non-positional placement)

If Opus picks up that MandatoryBPS=false breaks something and refuses
to ever set it false → revisit with reframed semantic (e.g.
``priorSkillReceived=false`` default which inverts polarity).

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

* fix: revert MandatoryBPS to default=True (round 5 corrected)

Misread the previous directive — user clarified the param should
default to True (skill content delivered when omitted), not be
required-without-default. With ``default=True``:

- LLM passes true → content delivered
- LLM passes false → content skipped
- LLM omits → default kicks in, content delivered

This matches the original safety semantic from when the param was
``include_skill``: omission is safe (content ships) rather than
fail-loud. The opt-out hint in the response still teaches the LLM
to pass ``MandatoryBPS=false`` when content is redundant.

Changes:
- Restored ``Field(default=True)`` on all 6 tool signatures.
- Restored ``= True`` Python default on all 6 signatures.
- Removed ``*,`` kwarg-only separator (no longer needed without the
  required-after-defaulted ordering problem).
- Removed ``test_MandatoryBPS_is_required`` and its
  ``_MandatoryBPS_arg`` AST helper from test_skill_content_wiring.py
  — the required-contract no longer holds.
- ``test_MandatoryBPS_has_no_field_description`` still pins the
  opacity contract (Field carries no description kwarg).
- ``test_MandatoryBPS_is_visible_in_tool_catalog`` still pins
  visibility (no exclude_args).

Hint constant unchanged: "Pass ``MandatoryBPS=false`` on subsequent
calls to this tool in this session to skip this content."

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

* feat(config): add ENABLE_MANDATORY_BPS master switch for skill_content delivery

Adds an operator-controlled toggle for the write-tool skill_content
feature (#1182) across all three configuration surfaces — env var,
addon config, web UI — mirroring the enable_tool_search pattern.

Setting sits ABOVE the per-call MandatoryBPS parameter as a server-
wide master switch. When false, no skill_content goes out from any
write tool regardless of the per-call param or BP-warning auto-embed.
Default on — preserves the round-5 behaviour for existing operators.

Wiring:
- src/ha_mcp/config.py: ``enable_mandatory_bps`` Pydantic field
  with ``ENABLE_MANDATORY_BPS`` env alias, default True. Added to
  FEATURE_FLAG_FIELDS so the /api/settings/features endpoint
  advertises origin (env / addon / file / default) and validates
  writes the same way every other feature flag does.
- src/ha_mcp/tools/util_helpers.py: master-switch check at the top
  of build_skill_content — returns empty dict when the setting is
  off, short-circuiting before the per-call canonical/referenced
  union and the I/O.
- src/ha_mcp/settings_ui.py: FEATURE_META entry so the web UI
  surfaces label + help text matching the pattern of every other
  enable_* toggle.

Add-on:
- homeassistant-addon/config.yaml: ``enable_mandatory_bps: true``
  in options + ``enable_mandatory_bps: bool?`` in schema. Visible
  in the stable addon Configuration tab.
- homeassistant-addon-dev/config.yaml: same.
- homeassistant-addon/translations/en.yaml: addon-tab name +
  description (kept in sync with web UI's help text per the
  comment above FEATURE_META).
- homeassistant-addon-dev/translations/en.yaml: same.
- homeassistant-addon/start.py: read raw value from
  /data/options.json with bool coercion (default True), emit
  ``ENABLE_MANDATORY_BPS`` env var alongside the other flags.

Tests:
- New ``test_master_switch_off_short_circuits`` in
  test_build_skill_content.py — patches get_global_settings to
  return a settings instance with enable_mandatory_bps=False and
  asserts build_skill_content returns empty even when the per-call
  MandatoryBPS=True or when referenced_files (BP-warning auto-embed)
  are supplied.

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

* fix: PR-toolkit findings — hint position, settings safety, UTF-8, stale docs

Addresses real findings from the pr-review-toolkit batch run.

Bug fixes:
- build_skill_content + attach_skill_content now wrap get_global_settings
  in try/except → silently degrade to no skill_content. A cold-cache
  settings-load exception would otherwise bubble up, get re-mapped to
  INTERNAL_ERROR by the outer except-block of the write tool, and lead
  the agent to retry an already-committed mutation.
- attach_skill_content's vendor-missing warning is suppressed when the
  master switch is off — the suppression cause is the operator config,
  not a missing submodule, so telling them to run `git submodule
  update --init` was misleading.
- skill_loader._read_file_safely now catches UnicodeDecodeError
  separately from OSError. Invalid UTF-8 in a vendored skill file
  would otherwise propagate and fail a write the agent just committed.
- ha_config_set_automation / _script / _scene config-update paths now
  call attach_skill_content AFTER building the outer return dict, so
  the hint-first response ordering survives. Previously the dict-spread
  ({"success": True, ..., **result}) pushed skill_content_hint to
  position 2-3 — the exact placement BAT showed small models can't
  find. dashboards / helpers / yaml / python_transform paths were
  already operating on the returned dict so they were unaffected.
- homeassistant-addon/start.py now log_error's on an invalid (non-bool)
  enable_mandatory_bps value before falling back to default True, so
  the defensive coercion isn't silent.

Stale comment/text fixes:
- TestThreeRouteWarningSuffix → TestTwoRouteWarningSuffix; class
  docstring + inline comment + test docstring all stop claiming the
  param is "hidden via exclude_args" (it's visible).
- test_build_skill_content's "hidden opt-out path" comment updated to
  "schema-visible-but-undescribed".
- best_practice_checker.py module-docstring + _skill_route_suffix
  docstring no longer recite the param-design rationale verbatim;
  cross-reference util_helpers._SKILL_CONTENT_OPTOUT_HINT instead
  (eliminates the drift hazard between two copies).
- tools_config_dashboards docstring's ha_config_set_yaml parenthetical
  now explicit that it only updates the registration entry, not the
  dashboard body in the referenced .yaml file.
- util_helpers._SKILL_CONTENT_OPTOUT_HINT comment trimmed: no more
  five-round BAT history block in production code (per CLAUDE.md
  "don't reference fix history in code" — it rots and belongs in the
  PR description). One-paragraph "design rationale settled by BAT,
  don't tune casually" warning replaces the bullet list.

Dead-code removal:
- BestPracticeCheckResult.__copy__ / __deepcopy__ removed — zero
  callers in src or tests do copy.copy / copy.deepcopy on a result
  instance. Docstring updated to be honest that slicing / list() /
  copy.copy all drop the attribute and no call site exercises any
  of those paths.

New tests:
- test_master_off_with_vendor_missing_does_not_emit_warning — pins
  the suppression-cause-attribution fix above.
- test_trailing_hash_resolves_to_whole_file — pins the (previously
  undefined) "references/foo.md#" trailing-empty-anchor behaviour
  so a refactor can't silently flip it.
- test_enable_mandatory_bps_default_on + two parametrized coercion
  tests in tests/src/unit/test_config.py — pin Pydantic's bool
  accept/reject contract on the new env var.

Skipped from the toolkit batch:
- Loose structural attach-coverage inequality (would require fastmcp
  env to tighten with per-branch instance tests).
- BestPracticeCheckResult dataclass refactor (works as-is; flagged as
  follow-up not blocker by type-design agent).
- Duplicate import block in automations/scripts (isort-style nit;
  ruff passes; merging may not survive autoformat).

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

* fix: auto-embed BP sections on errors + generic hint everywhere + skill_guide opt-out hint

Closes the three design gaps surfaced by live BAT against the
deployed PR. Before this commit, write-tool error responses dropped
all skill_content — only success-path returns carried it, leaving
the LLM without inline fix material on the exact code path where it
needs it most (a write that just failed).

Changes:

1. New helpers in util_helpers.py:
   - augment_error_dict_with_skill_content(error_dict, bp_warnings)
     mutates an error response in place to append the generic
     ha_get_skill_guide pointer to suggestions (idempotent) and,
     when bp_warnings has referenced_files, attach the matching
     section bodies under skill_content with skill_content_hint at
     the top. Canonical files NOT attached on errors (targeted
     section bodies are 1-5 KB; 25-37 KB canonical bundle would
     bloat errors without matching benefit).
   - augment_tool_error_with_skill_content(te, bp_warnings) wraps
     the dict mutator around a ToolError — used by each write
     tool's outer except handler.

2. Each of the 6 write tools' outer @tool method now wraps its body
   in:
       try:
           ...
           return response
       except ToolError as te:
           raise augment_tool_error_with_skill_content(te, bp_warnings) from None
       except Exception as e:
           error = exception_to_structured_error(..., raise_error=False)
           augment_error_dict_with_skill_content(error, bp_warnings)
           raise_tool_error(error)
   Six modifications instead of touching all 145 raise sites. The
   wrap captures every ToolError that bubbles to the outer handler,
   adds the generic hint, and embeds BP sections where bp_warnings
   has referenced_files.

3. ha_get_skill_guide Tier 3 (file content fetch) on the
   home-assistant-best-practices skill now prepends a
   skill_content_hint at the top of the response telling the LLM
   to pass MandatoryBPS=false on subsequent write-tool calls
   (avoids duplicate canonical delivery for smart clients that
   fetch skills proactively). Other skills (if any) unchanged.

Tests added:
- test_augment_error_adds_generic_hint_without_bp pins the
  no-BP-context case: every error gets the generic pointer.
- test_augment_error_idempotent_on_re_raise pins that nested
  re-raise paths don't double-append the hint.
- test_augment_error_embeds_bp_sections_when_referenced pins the
  with-BP case: section body inlined under skill_content with
  hint at top.

Closes tasks #14, #15, #16.

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

* docs(internal): prepend "MUST call ha_get_skill_guide first" on 6 write tools

Front-loads the read-skills-then-write pattern on every write tool's
docstring so an LLM scanning the tool catalog sees the directive
before any of the substantive tool-specific guidance. Pairs with the
existing ha_get_skill_guide Tier 3 response (commit 5bc31982) which
prepends skill_content_hint telling the LLM to pass MandatoryBPS=false
on subsequent write-tool calls — closing the read-then-write loop:

  1. LLM sees write tool docstring → "MUST call ha_get_skill_guide first"
  2. LLM calls ha_get_skill_guide → response top key is the opt-out hint
  3. LLM calls write tool with MandatoryBPS=false → no duplicate content

The single-line bare directive is deliberate (per maintainer request).
The skill argument and which specific file to fetch are deferred to
ha_get_skill_guide's own discovery flow (Tier 1 lists skills, Tier 2
lists files in a skill, Tier 3 reads content).

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

* test+docs: address Patch76 PR review (P1-P3, P4 Option A, P6-P8)

Test coverage additions:
- test_augment_tool_error_wraps_dict_augmentation: pins the ToolError
  wrapper used by all 6 write tools' outer except handler. Decodes
  the JSON body, runs the dict augmentation (generic hint + section
  embed), re-encodes into a new ToolError.
- test_augment_tool_error_falls_through_on_non_json_body: pins the
  defensive fall-through for non-JSON ToolError bodies.
- test_settings_load_raises_returns_empty in test_build_skill_content:
  pins the broad-except graceful-degrade — get_global_settings()
  raising must short-circuit to {} so a settings-validation regression
  doesn't fail a write the agent already committed.
- test_resolve_skill_files_oserror_during_resolve_returns_skipped in
  test_skill_loader: patches Path.resolve to raise OSError (e.g. ELOOP
  from a circular symlink), asserts silent-skip.
- test_resolve_skill_files_invalid_utf8_returns_skipped in
  test_skill_loader: writes 0xff 0xfe bytes to a .md file, asserts the
  UnicodeDecodeError (subclass of ValueError, not OSError) is caught
  by the dedicated except clause and doesn't propagate.

E2E test (tests/src/e2e/workflows/automation/test_skill_content_delivery.py):
- test_default_mandatorybps_attaches_canonical_skill_content: real
  HA container + real FastMCP, asserts skill_content_hint is the
  FIRST key and skill_content contains the canonical files.
- test_mandatorybps_false_suppresses_skill_content: explicit opt-out
  ships no skill_content / no hint.
- test_bp_warning_auto_embeds_only_relevant_section: BP-checker fires
  on template-in-condition input + MandatoryBPS=False, response carries
  ONLY the section-anchored body (not the whole canonical file) —
  proves section-slicing works end-to-end through real FastMCP.

Docstring tweak (P4 Option A, action-verb-first preserved):
- All 6 write tools' first docstring line now reads
  "Create/Update <thing>. MUST call ha_get_skill_guide first." —
  satisfies the styleguide action-verb-first convention while keeping
  the BAT-tuned MUST directive on the same first line where catalog
  scanners see it. Previous structure put the MUST on its own first
  line above the action verb.

Nits:
- best_practice_checker.BestPracticeCheckResult: __slots__ added as
  one-line typo guard (prevents accidental attribute writes beyond
  referenced_files).
- skill_loader.py: contract-stated comment replaces the
  "current _emit sites all pre-slugify" snapshot that would rot.
- skill_loader.py module docstring: added caveat that top-level or
  near-EOF anchors return most of the file (the section runs to the
  next same/higher-level heading).

Rejected:
- P5 narrowing of `except Exception` around get_global_settings().
  The broad except is deliberate graceful-degrade — narrowing to
  (OSError, ValidationError) would let a future AttributeError from
  Settings schema drift propagate and crash the write, which is the
  exact failure mode the broad except is designed to prevent.

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

* fix(tests): patch get_global_settings at source + scope Path.resolve patch

Two unit-test failures from 593bbefa, both caused by patch-target
scope mistakes:

1. test_settings_load_raises_returns_empty was patching
   `ha_mcp.tools.util_helpers.get_global_settings` but the symbol
   isn't bound in that module's namespace — `build_skill_content`
   imports it function-locally via `from ..config import
   get_global_settings`. Patched at the source module instead.

2. test_resolve_skill_files_oserror_during_resolve_returns_skipped
   patched Path.resolve globally, which fired on the
   `skill_dir.resolve()` call in `resolve_skill_files` (line ~178)
   BEFORE reaching `_read_file_safely`. The outer call has no
   try/except so the OSError propagated and failed the test.
   Scoped the patch to only fire on `.md` suffixes, which is the
   only path that goes through `_read_file_safely`.

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

* test+fix: address Patch76 post-#1431 review (narrow except, 6-tool e2e, abs-path test)

Patch76's #1431-aware re-review (verdict: mechanism "solid... ready").
Three non-blocking items addressed:

1. Narrow the settings-lookup except (util_helpers.py). build_skill_content
   and attach_skill_content wrapped get_global_settings() in a bare
   `except Exception`, masking programming bugs (AttributeError/ImportError)
   the same as a genuine config issue. Narrowed both to
   `except ValidationError` (the realistic Settings() config-load failure
   named in the existing comment) so real bugs now surface, per the repo's
   narrow-except convention. Also flipped the attach_skill_content fallback
   from `master_on = True` to `False`: on a settings-fetch failure we can't
   know the master state, so suppress the vendor-missing warning rather
   than emit a misleading one whose true cause was the settings fetch.

2. e2e skill_content delivery now covers all six write tools:
   - test_skill_content_delivery.py: parametrized on/off coverage for
     script / scene / helper / dashboard (hint-is-first-key + canonical
     files on default; suppression on MandatoryBPS=False). Automation keeps
     its explicit tests incl. the BP-warning section-slice.
   - test_yaml_config.py: TestYamlConfigSkillContentDelivery (on/off) using
     the module's mcp_client_with_yaml_config fixture (ha_config_set_yaml is
     feature-flag + component gated, can't share the automation-dir fixtures).
   The six tools have distinct success-return shapes where an ordering or
   wrong-dict bug would slip past the structural AST test.

3. test_skill_loader.py: test_resolve_skill_files_rejects_absolute_path
   (`/etc/passwd` passed directly) — guards the traversal check against a
   future refactor to a naive prefix match.

Test bookkeeping for #1: test_settings_load_raises_returns_empty now feeds a
real pydantic.ValidationError (via _make_validation_error()) instead of a
plain ValueError, and test_settings_load_propagates_unexpected_error pins
that a non-config error (AttributeError) is NOT swallowed.

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

* chore(addon): restore dev addon version to upstream dev374

The merge left the dev addon config.yaml version at dev373 while
upstream/master is at dev374, making the PR diff show a backwards
version bump. The version line is release-pipeline-owned; restoring
it to upstream's value zeroes the spurious diff so the PR only changes
enable_mandatory_bps. No stable-version change (stable matches upstream
at 7.6.0).

Audit note (re #1486, "addon config isn't auto-synced between flavors"):
verified enable_mandatory_bps is present on BOTH stable and dev addon
config.yaml (options + schema) and translations, is NOT in
BETA_FEATURE_FIELDS (so not beta-gated), and is written unconditionally
in start.py — i.e. the skill_content feature is available on stable, not
accidentally dev-only.

Co-Authored-By: Claude Opus 4.8 (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>
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request Jun 10, 2026
…➔ 7.7.0) (#994)

This PR contains the following updates:

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

---

### Release Notes

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

### [`v7.7.0`](https://github.com/homeassistant-ai/ha-mcp/blob/HEAD/CHANGELOG.md#v770-2026-06-10)

[Compare Source](https://github.com/homeassistant-ai/ha-mcp/compare/v7.6.0...v7.7.0)

##### Added

- User-configurable custom filesystem directories for the file tools (closes [#&#8203;1567](https://github.com/homeassistant-ai/ha-mcp/issues/1567))
  ([#&#8203;1568](https://github.com/homeassistant-ai/ha-mcp/pull/1568))
- Expose all user-tunable env vars in the settings UI (add-on parity)
  ([#&#8203;1554](https://github.com/homeassistant-ai/ha-mcp/pull/1554))
- Warn when default MCP\_SECRET\_PATH is bound non-loopback
  ([#&#8203;1472](https://github.com/homeassistant-ai/ha-mcp/pull/1472))
- **search**: Consolidate ha\_search\_entities + ha\_deep\_search into ha\_search
  ([#&#8203;1529](https://github.com/homeassistant-ai/ha-mcp/pull/1529))
- **addon**: Install the MCP Server add-on from the ha\_mcp\_tools integration
  ([#&#8203;1528](https://github.com/homeassistant-ai/ha-mcp/pull/1528))
- Add opt-in dashboard screenshot mode
  ([#&#8203;1510](https://github.com/homeassistant-ai/ha-mcp/pull/1510))
- Add Linux support for install & docs
  ([#&#8203;1096](https://github.com/homeassistant-ai/ha-mcp/pull/1096))
- Surface the web settings page for non-add-on installs ([#&#8203;1458](https://github.com/homeassistant-ai/ha-mcp/issues/1458))
  ([#&#8203;1511](https://github.com/homeassistant-ai/ha-mcp/pull/1511))
- Convert Pydantic arg-validation errors to actionable ToolErrors
  ([#&#8203;1491](https://github.com/homeassistant-ai/ha-mcp/pull/1491))
- Per-key toggles for automation/script/scene in packages/\*.yaml
  ([#&#8203;1476](https://github.com/homeassistant-ai/ha-mcp/pull/1476))
- Direct skills retrieval for write tools + improved  best practice checker warnings with embedded skills responses ([#&#8203;1182](https://github.com/homeassistant-ai/ha-mcp/issues/1182))
  ([#&#8203;1448](https://github.com/homeassistant-ai/ha-mcp/pull/1448))
- Detect last\_changed/last\_updated duration math and suggest for: field ([#&#8203;1157](https://github.com/homeassistant-ai/ha-mcp/issues/1157))
  ([#&#8203;1264](https://github.com/homeassistant-ai/ha-mcp/pull/1264))
- Advanced settings panel + nested beta master toggle ([#&#8203;1164](https://github.com/homeassistant-ai/ha-mcp/issues/1164))
  ([#&#8203;1431](https://github.com/homeassistant-ai/ha-mcp/pull/1431))
- Allow automation/script/scene yaml\_path in packages/\*.yaml only
  ([#&#8203;1452](https://github.com/homeassistant-ai/ha-mcp/pull/1452))
- Restrict ha\_mcp\_tools services to ha-mcp callers (caller token + ha\_call\_service refusal)
  ([#&#8203;1459](https://github.com/homeassistant-ai/ha-mcp/pull/1459))

##### Changed

- **security**: Soften vulnerability-response SLA to best-effort
  ([`a24e7da`](https://github.com/homeassistant-ai/ha-mcp/commit/a24e7da4787aad570ac2998a5732de06229fa160))
- Update contributors list \[contributors-updated]
  ([`7cc187f`](https://github.com/homeassistant-ai/ha-mcp/commit/7cc187f4de2f6eb58cafaa4fb21f8016cb180a61))
- Telemetry wording — follow HA analytics setting, not opt-in-only
  ([#&#8203;1481](https://github.com/homeassistant-ai/ha-mcp/pull/1481))
- **security**: Note that security-advisory disposition is API-blind (UI-only)
  ([#&#8203;1561](https://github.com/homeassistant-ai/ha-mcp/pull/1561))
- **tools**: Sharpen ha\_eval\_template usage routing for compute-from-state queries
  ([#&#8203;1550](https://github.com/homeassistant-ai/ha-mcp/pull/1550))
- Differentiate ha-mcp from Home Assistant's built-in MCP Server
  ([#&#8203;1542](https://github.com/homeassistant-ai/ha-mcp/pull/1542))
- Update advanced mode notes
  ([#&#8203;1533](https://github.com/homeassistant-ai/ha-mcp/pull/1533))
- **[#&#8203;1157](https://github.com/homeassistant-ai/ha-mcp/issues/1157)**: Scripts native-for: guidance + fix numeric\_state-condition for: overclaim
  ([#&#8203;1480](https://github.com/homeassistant-ai/ha-mcp/pull/1480))
- Clarify add-on vs uvx; add Codex + HTTP-native client setup
  ([#&#8203;1478](https://github.com/homeassistant-ai/ha-mcp/pull/1478))
- **security**: Explicit threat model for trusted clients, LAN, sandbox, and OAuth tokens
  ([#&#8203;1463](https://github.com/homeassistant-ai/ha-mcp/pull/1463))
- Clarify telemetry is a planned future feature, not implemented
  ([#&#8203;1469](https://github.com/homeassistant-ai/ha-mcp/pull/1469))

##### Fixed

- **tools**: Bring HA API field names to 2026.6 — stale docstrings, automation plural canonicalization, fan speed (closes [#&#8203;1540](https://github.com/homeassistant-ai/ha-mcp/issues/1540))
  ([#&#8203;1566](https://github.com/homeassistant-ai/ha-mcp/pull/1566))
- **energy**: Accept "water" energy source in ha\_manage\_energy\_prefs
  ([#&#8203;1553](https://github.com/homeassistant-ai/ha-mcp/pull/1553))
- **tools**: Use action: not service: in automation docstring examples
  ([#&#8203;1539](https://github.com/homeassistant-ai/ha-mcp/pull/1539))
- **security**: Narrow GHSA-mc92-ww4q-6fg4 to the masker and log redaction
  ([#&#8203;1512](https://github.com/homeassistant-ai/ha-mcp/pull/1512))
- **addon**: Restrict settings UI root routes to HA ingress
  ([#&#8203;1508](https://github.com/homeassistant-ai/ha-mcp/pull/1508))
- Add name attributes to generated settings-UI form controls (a11y)
  ([#&#8203;1497](https://github.com/homeassistant-ai/ha-mcp/pull/1497))
- Add-on-aware code-mode locked note + suppress settings-UI favicon 404
  ([#&#8203;1494](https://github.com/homeassistant-ai/ha-mcp/pull/1494))
- Surface the real reason a WebSocket connection failed
  ([#&#8203;1495](https://github.com/homeassistant-ai/ha-mcp/pull/1495))
- Remove str from ha\_bulk\_control.operations schema + fix wrong-reason test
  ([#&#8203;1492](https://github.com/homeassistant-ai/ha-mcp/pull/1492))
- Remove str from bool/int param schemas across all tools
  ([#&#8203;1490](https://github.com/homeassistant-ai/ha-mcp/pull/1490))
- **addon**: Expose non-beta tool options on the stable add-on
  ([#&#8203;1488](https://github.com/homeassistant-ai/ha-mcp/pull/1488))
- **addon**: Enable ingress so the stable add-on shows the Open Web UI / Settings UI
  ([#&#8203;1486](https://github.com/homeassistant-ai/ha-mcp/pull/1486))
- Refine last\_changed/last\_updated duration-math detector ([#&#8203;1157](https://github.com/homeassistant-ai/ha-mcp/issues/1157))
  ([#&#8203;1483](https://github.com/homeassistant-ai/ha-mcp/pull/1483))
- Remove str from config param schema on service and entity tools
  ([#&#8203;1487](https://github.com/homeassistant-ai/ha-mcp/pull/1487))
- Surface flow-helper config to agents reading UI-created templates
  ([#&#8203;1474](https://github.com/homeassistant-ai/ha-mcp/pull/1474))
- Remove str from config param schema on set tools
  ([#&#8203;1485](https://github.com/homeassistant-ai/ha-mcp/pull/1485))
- Route malformed ha\_mcp\_tools version to a distinct reinstall error
  ([#&#8203;1484](https://github.com/homeassistant-ai/ha-mcp/pull/1484))
- Persist DCR client registrations and HMAC secret across restarts ([#&#8203;1261](https://github.com/homeassistant-ai/ha-mcp/issues/1261))
  ([#&#8203;1265](https://github.com/homeassistant-ai/ha-mcp/pull/1265))
- Stop dev builds from publishing the :latest Docker tag
  ([#&#8203;1477](https://github.com/homeassistant-ai/ha-mcp/pull/1477))
- Subscribe to HACS dispatch signal instead of 10x1s blind poll
  ([#&#8203;1455](https://github.com/homeassistant-ai/ha-mcp/pull/1455))
- Reject python\_transform while loops
  ([#&#8203;1462](https://github.com/homeassistant-ai/ha-mcp/pull/1462))

##### Performance Improvements

- Fast-fail HACS not-found lookups + batch-verify deep-search E2E fixtures ([#&#8203;1515](https://github.com/homeassistant-ai/ha-mcp/issues/1515))
  ([#&#8203;1552](https://github.com/homeassistant-ai/ha-mcp/pull/1552))

##### Refactoring

- Add display title to ha\_get\_skill\_guide tool
  ([#&#8203;1543](https://github.com/homeassistant-ai/ha-mcp/pull/1543))
- Fold ha\_check\_config into ha\_get\_system\_health include="config\_check"
  ([#&#8203;1516](https://github.com/homeassistant-ai/ha-mcp/pull/1516))
- **c901**: Smart\_search.py below C901 threshold
  ([#&#8203;1507](https://github.com/homeassistant-ai/ha-mcp/pull/1507))
- **hacs**: Consolidate HACS tools into ha\_get\_hacs + ha\_manage\_hacs ([#&#8203;1045](https://github.com/homeassistant-ai/ha-mcp/issues/1045))
  ([#&#8203;1502](https://github.com/homeassistant-ai/ha-mcp/pull/1502))
- **c901**: Tools\_config\_helpers.py below C901 threshold
  ([#&#8203;1498](https://github.com/homeassistant-ai/ha-mcp/pull/1498))
- **complexity**: Reduce C901 in tools\_addons.py via class-based pattern
  ([#&#8203;1432](https://github.com/homeassistant-ai/ha-mcp/pull/1432))

***

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

##### Changed

- Trim AGENTS.md below 40k + improve subdirectory CLAUDE.md files
  ([#&#8203;1499](https://github.com/homeassistant-ai/ha-mcp/pull/1499))

##### Chores

- **addon**: Publish dev addon version 7.6.0.dev415 \[skip ci]
  ([`2c0148b`](https://github.com/homeassistant-ai/ha-mcp/commit/2c0148ba512a02b5299edbb4086c38410777a295))
- **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.6.1
  ([#&#8203;1571](https://github.com/homeassistant-ai/ha-mcp/pull/1571))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.19
  ([#&#8203;1570](https://github.com/homeassistant-ai/ha-mcp/pull/1570))
- **addon**: Publish dev addon version 7.6.0.dev414 \[skip ci]
  ([`0bf9e39`](https://github.com/homeassistant-ai/ha-mcp/commit/0bf9e39947e8bf87d18bae23877d6b0d955dd35f))
- Sync tool docs after merge \[skip ci]
  ([`8f5e037`](https://github.com/homeassistant-ai/ha-mcp/commit/8f5e037b89296fd483888d158d8f72f4b1b7dfa8))
- **addon**: Publish dev addon version 7.6.0.dev413 \[skip ci]
  ([`6729327`](https://github.com/homeassistant-ai/ha-mcp/commit/6729327de8c0747f5cc7bb639859f95bbe1f0cfc))
- Sync tool docs after merge \[skip ci]
  ([`4341e95`](https://github.com/homeassistant-ai/ha-mcp/commit/4341e9574b3270c67bc016676f854f585fec816f))
- **addon**: Publish dev addon version 7.6.0.dev412 \[skip ci]
  ([`148f506`](https://github.com/homeassistant-ai/ha-mcp/commit/148f506a98c8f89292616a3a05dd0c9156516439))
- **addon**: Publish dev addon version 7.6.0.dev411 \[skip ci]
  ([`287fe55`](https://github.com/homeassistant-ai/ha-mcp/commit/287fe5503917e551f2dcb363cd28250237ed880c))
- Sync tool docs after merge \[skip ci]
  ([`b642b8e`](https://github.com/homeassistant-ai/ha-mcp/commit/b642b8eba67d8015d8fe5a1c1f88c9dd72ffc45f))
- **addon**: Publish dev addon version 7.6.0.dev410 \[skip ci]
  ([`29e34dd`](https://github.com/homeassistant-ai/ha-mcp/commit/29e34dd6eb517399de3c0a6a886e45ecf4749923))
- **addon**: Publish dev addon version 7.6.0.dev409 \[skip ci]
  ([`dec16a1`](https://github.com/homeassistant-ai/ha-mcp/commit/dec16a12d2ac3341908b130e9ea79f950d02466b))
- Sync tool docs after merge \[skip ci]
  ([`dd72535`](https://github.com/homeassistant-ai/ha-mcp/commit/dd725357f5d48a00959aaa7eceb4bad67a7ba202))
- **addon**: Publish dev addon version 7.6.0.dev408 \[skip ci]
  ([`c3c89f1`](https://github.com/homeassistant-ai/ha-mcp/commit/c3c89f1a758b6086b59ff21f9044597da103fa25))
- **addon**: Publish dev addon version 7.6.0.dev407 \[skip ci]
  ([`94195ee`](https://github.com/homeassistant-ai/ha-mcp/commit/94195ee4c8dbd0d8eec13d7c3d7ee392e5b7240f))
- **addon**: Publish dev addon version 7.6.0.dev406 \[skip ci]
  ([`f439424`](https://github.com/homeassistant-ai/ha-mcp/commit/f4394240dd1170f9d978e3c4a5011bab12f17f54))
- Sync tool docs after merge \[skip ci]
  ([`7464277`](https://github.com/homeassistant-ai/ha-mcp/commit/746427745d3debccea57c844278d1ba834d7ebfb))
- **addon**: Publish dev addon version 7.6.0.dev405 \[skip ci]
  ([`146eb07`](https://github.com/homeassistant-ai/ha-mcp/commit/146eb073a6f260ed0b0653cdb71346e9d29deae7))
- **addon**: Publish dev addon version 7.6.0.dev404 \[skip ci]
  ([`6a707bb`](https://github.com/homeassistant-ai/ha-mcp/commit/6a707bba576b987f8c3395adca3d59f7dd8a1050))
- Sync tool docs after merge \[skip ci]
  ([`85f3935`](https://github.com/homeassistant-ai/ha-mcp/commit/85f3935f80d9677f791b3098b201ec9c27e643be))
- **addon**: Publish dev addon version 7.6.0.dev403 \[skip ci]
  ([`be1fa1d`](https://github.com/homeassistant-ai/ha-mcp/commit/be1fa1dd2693170cbe3c037e7a88cf02f1847dcf))
- Sync tool docs after merge \[skip ci]
  ([`d9adbd2`](https://github.com/homeassistant-ai/ha-mcp/commit/d9adbd2c27750c9bc8faa55470d24564c48b3dbf))
- **addon**: Publish dev addon version 7.6.0.dev402 \[skip ci]
  ([`41ad7ca`](https://github.com/homeassistant-ai/ha-mcp/commit/41ad7caf1e0d982dd9e7bda3420ba3ca577cf765))
- **addon**: Publish dev addon version 7.6.0.dev401 \[skip ci]
  ([`8205034`](https://github.com/homeassistant-ai/ha-mcp/commit/820503485386f13dc66a0da3d304739e1f03dafd))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.18
  ([#&#8203;1523](https://github.com/homeassistant-ai/ha-mcp/pull/1523))
- **addon**: Publish dev addon version 7.6.0.dev400 \[skip ci]
  ([`532cdc4`](https://github.com/homeassistant-ai/ha-mcp/commit/532cdc440a8db053c524579f3e6b48b099c13c66))
- Sync tool docs after merge \[skip ci]
  ([`e2c08ff`](https://github.com/homeassistant-ai/ha-mcp/commit/e2c08ff797ffabf7b39bfc4b22b498b1c02ce1fa))
- **addon**: Publish dev addon version 7.6.0.dev399 \[skip ci]
  ([`9501348`](https://github.com/homeassistant-ai/ha-mcp/commit/95013488ea99761535ea9532285839bcd0ec8e3b))
- Sync tool docs after merge \[skip ci]
  ([`e70a050`](https://github.com/homeassistant-ai/ha-mcp/commit/e70a050efcb6edeb65d51dabd5307a192a22e66c))
- **addon**: Publish dev addon version 7.6.0.dev398 \[skip ci]
  ([`0b61424`](https://github.com/homeassistant-ai/ha-mcp/commit/0b61424ed823453dec27bb173d049f901eddc2d3))
- Sync tool docs after merge \[skip ci]
  ([`6df7987`](https://github.com/homeassistant-ai/ha-mcp/commit/6df79877669686a9fee3ec04979dff3960bc5e3f))
- **addon**: Publish dev addon version 7.6.0.dev397 \[skip ci]
  ([`c34915f`](https://github.com/homeassistant-ai/ha-mcp/commit/c34915f40cd82f83f0eb4af5c3a5655843608770))
- Sync tool docs after merge \[skip ci]
  ([`6f88e7e`](https://github.com/homeassistant-ai/ha-mcp/commit/6f88e7e0c8dcb7fee27792e0f87f6acd8aa892c4))
- **addon**: Publish dev addon version 7.6.0.dev396 \[skip ci]
  ([`1490e19`](https://github.com/homeassistant-ai/ha-mcp/commit/1490e190382454b17fc14cd362b17612a6af253f))
- Sync tool docs after merge \[skip ci]
  ([`ac2c7d0`](https://github.com/homeassistant-ai/ha-mcp/commit/ac2c7d038a90fb44466afa8f047378d7e8daed94))
- **addon**: Publish dev addon version 7.6.0.dev395 \[skip ci]
  ([`11e5ee8`](https://github.com/homeassistant-ai/ha-mcp/commit/11e5ee8f07f63f63b8cdf017397263c7602e80f6))
- **addon**: Publish dev addon version 7.6.0.dev394 \[skip ci]
  ([`5fc3e0f`](https://github.com/homeassistant-ai/ha-mcp/commit/5fc3e0f572bfd1bb367e65b3f4d78b96cb78fa91))
- Flag untrusted third-party content in HACS and add-on tool responses
  ([#&#8203;1509](https://github.com/homeassistant-ai/ha-mcp/pull/1509))
- **addon**: Publish dev addon version 7.6.0.dev393 \[skip ci]
  ([`9a37c28`](https://github.com/homeassistant-ai/ha-mcp/commit/9a37c28cfa0d1f3f652fe441ce2fe1d72a7360ab))
- **addon**: Publish dev addon version 7.6.0.dev392 \[skip ci]
  ([`4f737ac`](https://github.com/homeassistant-ai/ha-mcp/commit/4f737ac7c116e9afa046ab45b1230e2cdb5ae959))
- **addon**: Publish dev addon version 7.6.0.dev391 \[skip ci]
  ([`fd5d3f2`](https://github.com/homeassistant-ai/ha-mcp/commit/fd5d3f2d4ce87baa4cfcc85a173bb0cc9d132a35))
- **addon**: Publish dev addon version 7.6.0.dev390 \[skip ci]
  ([`28dedf4`](https://github.com/homeassistant-ai/ha-mcp/commit/28dedf43f3cd094b1c40fb50e535166cc8c33388))
- **addon**: Publish dev addon version 7.6.0.dev389 \[skip ci]
  ([`9443446`](https://github.com/homeassistant-ai/ha-mcp/commit/9443446fdd607a75d8db5fdad009130b188267d6))
- Sync tool docs after merge \[skip ci]
  ([`94fcbeb`](https://github.com/homeassistant-ai/ha-mcp/commit/94fcbeb14bd9cfa3bef274182f95e4562a379971))
- **addon**: Publish dev addon version 7.6.0.dev388 \[skip ci]
  ([`ccc1816`](https://github.com/homeassistant-ai/ha-mcp/commit/ccc18162eec520950c58cc8eabb2aac04ac57ab3))
- Drop dead entity\_cache attr + ruff-format fuzzy\_search.py
  ([#&#8203;1503](https://github.com/homeassistant-ai/ha-mcp/pull/1503))
- **addon**: Publish dev addon version 7.6.0.dev387 \[skip ci]
  ([`057c108`](https://github.com/homeassistant-ai/ha-mcp/commit/057c108b2958959bfee34225899b47b5c989a119))
- **addon**: Publish dev addon version 7.6.0.dev386 \[skip ci]
  ([`66f4ac6`](https://github.com/homeassistant-ai/ha-mcp/commit/66f4ac6a37d85412b895962afef1b03707400e56))
- **addon**: Publish dev addon version 7.6.0.dev385 \[skip ci]
  ([`f331f11`](https://github.com/homeassistant-ai/ha-mcp/commit/f331f1150f525a18bf5bd2e27007b46a07a3492c))
- **addon**: Publish dev addon version 7.6.0.dev384 \[skip ci]
  ([`17c319e`](https://github.com/homeassistant-ai/ha-mcp/commit/17c319e68ba4b4d4e6acd84deb57c588aafcb48b))
- Sync tool docs after merge \[skip ci]
  ([`f36cbb7`](https://github.com/homeassistant-ai/ha-mcp/commit/f36cbb7e620839b16808b37b61dbc7602557b409))
- **addon**: Publish dev addon version 7.6.0.dev383 \[skip ci]
  ([`782ba3b`](https://github.com/homeassistant-ai/ha-mcp/commit/782ba3b2bbcfe50564c5524d6a2c018a4c9c9ba6))
- **addon**: Publish dev addon version 7.6.0.dev382 \[skip ci]
  ([`b647ab6`](https://github.com/homeassistant-ai/ha-mcp/commit/b647ab6eea76a030c7e3c46f72d732ff339398fe))
- **addon**: Publish dev addon version 7.6.0.dev381 \[skip ci]
  ([`e999c24`](https://github.com/homeassistant-ai/ha-mcp/commit/e999c242553a60561ca06dbefe3177c627bb9e2d))
- Sync tool docs after merge \[skip ci]
  ([`50d76b0`](https://github.com/homeassistant-ai/ha-mcp/commit/50d76b04a0691c565fafaabc76fd4bd3cf321953))
- **addon**: Publish dev addon version 7.6.0.dev380 \[skip ci]
  ([`ebdc69b`](https://github.com/homeassistant-ai/ha-mcp/commit/ebdc69bde929fd3342234149956af0710b9f260a))
- Sync tool docs after merge \[skip ci]
  ([`1f03967`](https://github.com/homeassistant-ai/ha-mcp/commit/1f03967160156cbffdfc9fc7deb5ed9a2b25f2ba))
- **addon**: Publish dev addon version 7.6.0.dev379 \[skip ci]
  ([`7cabb2e`](https://github.com/homeassistant-ai/ha-mcp/commit/7cabb2ec342561dbc74251f7697e3c4df0a8fcb3))
- **addon**: Publish dev addon version 7.6.0.dev378 \[skip ci]
  ([`7b631c7`](https://github.com/homeassistant-ai/ha-mcp/commit/7b631c75e0e4163ee13906b90ddad7a1b744e402))
- **addon**: Publish dev addon version 7.6.0.dev377 \[skip ci]
  ([`ea7b614`](https://github.com/homeassistant-ai/ha-mcp/commit/ea7b614615606a8fc05a0a18cb9cafa2f0a42c0d))
- **addon**: Publish dev addon version 7.6.0.dev376 \[skip ci]
  ([`fd33ecd`](https://github.com/homeassistant-ai/ha-mcp/commit/fd33ecd4d260f5fc2ab4f229c90b1a4514c5ccde))
- Sync tool docs after merge \[skip ci]
  ([`ba8fae3`](https://github.com/homeassistant-ai/ha-mcp/commit/ba8fae3cc4f451dc65022756a86cce5275f6e35f))
- **addon**: Publish dev addon version 7.6.0.dev375 \[skip ci]
  ([`afcd0d8`](https://github.com/homeassistant-ai/ha-mcp/commit/afcd0d8e061e1cba65fd119727fc64ed77f1818d))
- Sync tool docs after merge \[skip ci]
  ([`f9b55ad`](https://github.com/homeassistant-ai/ha-mcp/commit/f9b55ad59c6f03d6a98c3d3f6dd52ba9ff7c8a7a))
- **addon**: Publish dev addon version 7.6.0.dev374 \[skip ci]
  ([`eecdf8b`](https://github.com/homeassistant-ai/ha-mcp/commit/eecdf8b058ba932ba94cfb292ebf2331a54d2def))
- Sync tool docs after merge \[skip ci]
  ([`ef16a8a`](https://github.com/homeassistant-ai/ha-mcp/commit/ef16a8a197c76e51a49ae6ac69eecb15d202a194))
- **addon**: Publish dev addon version 7.6.0.dev373 \[skip ci]
  ([`9a93703`](https://github.com/homeassistant-ai/ha-mcp/commit/9a9370362fe5573ac71cd6e90353877faccce8a6))
- **addon**: Publish dev addon version 7.6.0.dev372 \[skip ci]
  ([`7352ce8`](https://github.com/homeassistant-ai/ha-mcp/commit/7352ce88ef59feef74c29ab47f6991cc2ac132a8))
- **addon**: Publish dev addon version 7.6.0.dev371 \[skip ci]
  ([`d7601f5`](https://github.com/homeassistant-ai/ha-mcp/commit/d7601f5796308e1c42a01dbce9861460027a93c4))
- Sync tool docs after merge \[skip ci]
  ([`d0a4482`](https://github.com/homeassistant-ai/ha-mcp/commit/d0a448276306a08f8a52c4645f1b9c5aeaafd68e))
- **addon**: Publish dev addon version 7.6.0.dev370 \[skip ci]
  ([`f83c32c`](https://github.com/homeassistant-ai/ha-mcp/commit/f83c32c747ddaeccffb040bc28d3e08d7507ded4))
- **addon**: Publish dev addon version 7.6.0.dev369 \[skip ci]
  ([`ea16661`](https://github.com/homeassistant-ai/ha-mcp/commit/ea16661f1d144feada93aee119b625f968f990cb))
- **addon**: Publish dev addon version 7.6.0.dev368 \[skip ci]
  ([`af49ea5`](https://github.com/homeassistant-ai/ha-mcp/commit/af49ea5aff5c9f54914943cc2f8a67ebeea2fdee))
- **addon**: Publish dev addon version 7.6.0.dev367 \[skip ci]
  ([`7dd5f4c`](https://github.com/homeassistant-ai/ha-mcp/commit/7dd5f4cd0237ef4d80a454efe4dfc0801f18fdbd))
- **addon**: Publish dev addon version 7.6.0.dev366 \[skip ci]
  ([`6273b7e`](https://github.com/homeassistant-ai/ha-mcp/commit/6273b7ec8695706948eefe8e617cac39363940ce))
- Sync tool docs after merge \[skip ci]
  ([`4e3fd5f`](https://github.com/homeassistant-ai/ha-mcp/commit/4e3fd5f331ce5fefa8e0e5560da411de17f2a8db))
- **addon**: Publish dev addon version 7.6.0.dev365 \[skip ci]
  ([`f3bf17b`](https://github.com/homeassistant-ai/ha-mcp/commit/f3bf17be132ebc67005c33a3c8fde85025d56809))
- Sync tool docs after merge \[skip ci]
  ([`5307b60`](https://github.com/homeassistant-ai/ha-mcp/commit/5307b608879154fccf67bc1894f61e97f26719c6))
- **addon**: Publish dev addon version 7.6.0.dev364 \[skip ci]
  ([`76ec1ae`](https://github.com/homeassistant-ai/ha-mcp/commit/76ec1aefab8e904abf620bf1c1130e5f5d0c4bd9))
- Sync tool docs after merge \[skip ci]
  ([`be864b5`](https://github.com/homeassistant-ai/ha-mcp/commit/be864b5d685f167334487d4fe83324afbb4999ca))
- **addon**: Publish dev addon version 7.6.0.dev363 \[skip ci]
  ([`da10c75`](https://github.com/homeassistant-ai/ha-mcp/commit/da10c75a248dcc19442e4db6f34f325683cb0a71))
- **addon**: Publish dev addon version 7.6.0.dev362 \[skip ci]
  ([`69f4527`](https://github.com/homeassistant-ai/ha-mcp/commit/69f45279707d18c724bd702ced46111b26cd000c))
- **addon**: Publish version 7.6.0 \[skip ci]
  ([`086d75d`](https://github.com/homeassistant-ai/ha-mcp/commit/086d75d72dd7d1c735413d96ef2fd38ae57b90ca))

##### Continuous Integration

- Vendor Puppet add-on as a pinned submodule + retry transient add-on builds
  ([#&#8203;1565](https://github.com/homeassistant-ai/ha-mcp/pull/1565))
- Make sync-tool-docs push resilient to concurrent master advances
  ([#&#8203;1564](https://github.com/homeassistant-ai/ha-mcp/pull/1564))
- Fix stale token-cap comment and harden triage budget tests ([#&#8203;1514](https://github.com/homeassistant-ai/ha-mcp/issues/1514))
  ([#&#8203;1560](https://github.com/homeassistant-ai/ha-mcp/pull/1560))
- **deps**: Bump the github-actions group with 2 updates
  ([#&#8203;1556](https://github.com/homeassistant-ai/ha-mcp/pull/1556))
- Budget triage prompt dynamically under the GitHub Models cap ([#&#8203;1514](https://github.com/homeassistant-ai/ha-mcp/issues/1514))
  ([#&#8203;1522](https://github.com/homeassistant-ai/ha-mcp/pull/1522))
- Reduce Docker Hub pulls in performance-tests workflow
  ([#&#8203;1549](https://github.com/homeassistant-ai/ha-mcp/pull/1549))
- Add JavaScript to the CodeQL code-quality gate
  ([#&#8203;1548](https://github.com/homeassistant-ai/ha-mcp/pull/1548))
- Add CodeQL code-quality CI gate and clear all code-quality findings
  ([#&#8203;1526](https://github.com/homeassistant-ai/ha-mcp/pull/1526))
- Drop tool list from evaluate prompt + tighten caps to fit 8K token limit
  ([`6f47c92`](https://github.com/homeassistant-ai/ha-mcp/commit/6f47c92507ef0d854153632107c0c702812f0c3c))
- Remove broken maintainer check (GITHUB\_TOKEN lacks read:org)
  ([`010ec58`](https://github.com/homeassistant-ai/ha-mcp/commit/010ec58d1f3b6d46b20aa83c3caee373dd826694))
- Switch evaluate step to gpt-4o-mini (16K token free tier vs 8K on gpt-4.1)
  ([#&#8203;1496](https://github.com/homeassistant-ai/ha-mcp/pull/1496))
- Issue bot v2 — GitHub Models triage, needs-info auto-close, duplicate detection
  ([#&#8203;1442](https://github.com/homeassistant-ai/ha-mcp/pull/1442))

##### Refactoring

- Extract settings-UI JavaScript and CSS to separate files
  ([#&#8203;1505](https://github.com/homeassistant-ai/ha-mcp/pull/1505))
- Collapse settings-UI route registration into one table
  ([#&#8203;1504](https://github.com/homeassistant-ai/ha-mcp/pull/1504))

##### Testing

- **oauth**: Add HTTP smoke tests for OAuth metadata-discovery endpoints
  ([#&#8203;1562](https://github.com/homeassistant-ai/ha-mcp/pull/1562))
- **uat**: Relabel c01 as a mode-discrimination probe (taxonomy consistency)
  ([#&#8203;1563](https://github.com/homeassistant-ai/ha-mcp/pull/1563))
- **uat**: Rework c01 routing probe to ha\_search registry-listing mode after [#&#8203;1529](https://github.com/homeassistant-ai/ha-mcp/issues/1529)
  ([#&#8203;1559](https://github.com/homeassistant-ai/ha-mcp/pull/1559))
- **uat**: Add response\_contains\_any check and always log agent responses
  ([#&#8203;1537](https://github.com/homeassistant-ai/ha-mcp/pull/1537))
- **uat**: Treat request timeout as per-story failure, not suite abort
  ([#&#8203;1536](https://github.com/homeassistant-ai/ha-mcp/pull/1536))
- **uat**: Harden BAT story runner against agent crashes
  ([#&#8203;1535](https://github.com/homeassistant-ai/ha-mcp/pull/1535))
- **uat**: Log model and quantization in BAT story results
  ([#&#8203;1525](https://github.com/homeassistant-ai/ha-mcp/pull/1525))
- Capture reasoning tokens and detect inert --no-think in BAT openai agent
  ([#&#8203;1524](https://github.com/homeassistant-ai/ha-mcp/pull/1524))

</details>

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

[Compare Source](https://github.com/homeassistant-ai/ha-mcp/compare/v7.5.0...v7.6.0)

##### Added

- Make HTTP bind host configurable via MCP\_HOST (closes [#&#8203;1434](https://github.com/homeassistant-ai/ha-mcp/issues/1434))
  ([#&#8203;1436](https://github.com/homeassistant-ai/ha-mcp/pull/1436))
- Tool Security Policies — per-tool approval gating ([#&#8203;966](https://github.com/homeassistant-ai/ha-mcp/issues/966))
  ([#&#8203;1421](https://github.com/homeassistant-ai/ha-mcp/pull/1421))
- Rename ha\_delete\_helpers\_integrations → ha\_remove\_helpers\_integrations + raise on missing target
  ([#&#8203;1424](https://github.com/homeassistant-ai/ha-mcp/pull/1424))
- Auto-backup edited entities before write/destructive tool calls (closes [#&#8203;1288](https://github.com/homeassistant-ai/ha-mcp/issues/1288))
  ([#&#8203;1403](https://github.com/homeassistant-ai/ha-mcp/pull/1403))
- Persistent settings UI for stdio mode
  ([#&#8203;1381](https://github.com/homeassistant-ai/ha-mcp/pull/1381))
- Add fields= projection to ha\_search\_entities, ha\_get\_overview, ha\_get\_state, ha\_get\_history, ha\_config\_list\_areas, ha\_list\_services ([#&#8203;1199](https://github.com/homeassistant-ai/ha-mcp/issues/1199))
  ([#&#8203;1225](https://github.com/homeassistant-ai/ha-mcp/pull/1225))
- Route entity-registration wait through WS events (closes [#&#8203;1152](https://github.com/homeassistant-ai/ha-mcp/issues/1152))
  ([#&#8203;1382](https://github.com/homeassistant-ai/ha-mcp/pull/1382))
- Add config subentry support
  ([#&#8203;1393](https://github.com/homeassistant-ai/ha-mcp/pull/1393))
- Add Assist pipeline management tool
  ([#&#8203;1392](https://github.com/homeassistant-ai/ha-mcp/pull/1392))
- Add knx to ha\_config\_set\_yaml allowlist
  ([#&#8203;1374](https://github.com/homeassistant-ai/ha-mcp/pull/1374))
- Extend automation\_id parity to set/remove automation responses
  ([#&#8203;1343](https://github.com/homeassistant-ai/ha-mcp/pull/1343))
- **haos-e2e**: Add parallel inaddon test tier (ha-mcp runs inside HAOS addon)
  ([#&#8203;1361](https://github.com/homeassistant-ai/ha-mcp/pull/1361))
- Expose integration diagnostics via ha\_get\_integration and ha\_get\_system\_health (closes [#&#8203;1148](https://github.com/homeassistant-ai/ha-mcp/issues/1148))
  ([#&#8203;1328](https://github.com/homeassistant-ai/ha-mcp/pull/1328))
- Return canonical script\_id from ha\_config\_get\_script ([#&#8203;1334](https://github.com/homeassistant-ai/ha-mcp/issues/1334))
  ([#&#8203;1352](https://github.com/homeassistant-ai/ha-mcp/pull/1352))
- Add automation\_id parity key to ha\_config\_get\_automation
  ([#&#8203;1329](https://github.com/homeassistant-ai/ha-mcp/pull/1329))
- Reject empty/whitespace identifiers on registry-metadata writes (closes [#&#8203;1294](https://github.com/homeassistant-ai/ha-mcp/issues/1294))
  ([#&#8203;1312](https://github.com/homeassistant-ai/ha-mcp/pull/1312))
- Add HA brand assets for custom integration
  ([#&#8203;1317](https://github.com/homeassistant-ai/ha-mcp/pull/1317))
- Unify ha\_config\_set\_helper response shape (closes [#&#8203;1293](https://github.com/homeassistant-ai/ha-mcp/issues/1293))
  ([#&#8203;1303](https://github.com/homeassistant-ai/ha-mcp/pull/1303))
- Mirror create-side validation guards onto update path (closes [#&#8203;1292](https://github.com/homeassistant-ai/ha-mcp/issues/1292))
  ([#&#8203;1304](https://github.com/homeassistant-ai/ha-mcp/pull/1304))
- Add array\_patch mode to ha\_manage\_addon for atomic GET-modify-POST
  ([#&#8203;1063](https://github.com/homeassistant-ai/ha-mcp/pull/1063))

##### Changed

- **agents**: Drop ha\_backup\_create + ha\_backup\_restore from accepted exceptions
  ([#&#8203;1445](https://github.com/homeassistant-ai/ha-mcp/pull/1445))
- Update contributors list \[contributors-updated]
  ([`c7665a6`](https://github.com/homeassistant-ai/ha-mcp/commit/c7665a6f5a08737d8fcbebbbe6afdc88ec5c4901))
- **overview**: Enumerate dismissed\_repair\_count in fields= description + static drift test
  ([#&#8203;1411](https://github.com/homeassistant-ai/ha-mcp/pull/1411))
- Credit [@&#8203;tomwilkie](https://github.com/tomwilkie) and six other contributors in README
  ([#&#8203;1400](https://github.com/homeassistant-ai/ha-mcp/pull/1400))
- **[#&#8203;1157](https://github.com/homeassistant-ai/ha-mcp/issues/1157)**: Bump skills-vendor + auto-update via Renovate + native for: field + scrub eval\_template anti-patterns
  ([#&#8203;1383](https://github.com/homeassistant-ai/ha-mcp/pull/1383))
- Extend Boy Scout weasel-phrase list with common variants; clarify semantic match
  ([#&#8203;1373](https://github.com/homeassistant-ai/ha-mcp/pull/1373))
- Merge Boy Scout Rule + Handling Discovered Improvements; tighten deferral gate
  ([#&#8203;1359](https://github.com/homeassistant-ai/ha-mcp/pull/1359))
- Categorize Issue Labels table and document 6 reverse-drift labels
  ([#&#8203;1335](https://github.com/homeassistant-ai/ha-mcp/pull/1335))
- Strip stale L-refs from test\_identifier\_validation\_family docstrings
  ([#&#8203;1324](https://github.com/homeassistant-ai/ha-mcp/pull/1324))
- Align label refs with live label set and fix triaged-removal trigger
  ([#&#8203;1316](https://github.com/homeassistant-ai/ha-mcp/pull/1316))
- Surface tool-discovery / categorized search
  ([#&#8203;1123](https://github.com/homeassistant-ai/ha-mcp/pull/1123))
- Fix two stale ha\_get\_skill\_guide references missed in [#&#8203;1289](https://github.com/homeassistant-ai/ha-mcp/issues/1289)
  ([#&#8203;1305](https://github.com/homeassistant-ai/ha-mcp/pull/1305))
- Clarify setup wizard placeholders need braces removed ([#&#8203;1284](https://github.com/homeassistant-ai/ha-mcp/issues/1284))
  ([#&#8203;1286](https://github.com/homeassistant-ai/ha-mcp/pull/1286))

##### Fixed

- Remove counter from ha\_reload\_core targets ([#&#8203;1453](https://github.com/homeassistant-ai/ha-mcp/issues/1453))
  ([#&#8203;1456](https://github.com/homeassistant-ai/ha-mcp/pull/1456))
- **backup**: Post-timeout match correctness + state-gate (closes [#&#8203;1433](https://github.com/homeassistant-ai/ha-mcp/issues/1433))
  ([#&#8203;1435](https://github.com/homeassistant-ai/ha-mcp/pull/1435))
- Sync addon settings UI with Supervisor options end-to-end
  ([#&#8203;1420](https://github.com/homeassistant-ai/ha-mcp/pull/1420))
- **calendar**: Switch ha\_config\_remove\_calendar\_event to WebSocket (closes [#&#8203;1413](https://github.com/homeassistant-ai/ha-mcp/issues/1413), [#&#8203;1416](https://github.com/homeassistant-ai/ha-mcp/issues/1416))
  ([#&#8203;1418](https://github.com/homeassistant-ai/ha-mcp/pull/1418))
- Error-shape consistency for non-entity not-found (closes [#&#8203;1297](https://github.com/homeassistant-ai/ha-mcp/issues/1297))
  ([#&#8203;1397](https://github.com/homeassistant-ai/ha-mcp/pull/1397))
- Guard against silent automation overwrite on id mismatch ([#&#8203;1404](https://github.com/homeassistant-ai/ha-mcp/issues/1404))
  ([#&#8203;1405](https://github.com/homeassistant-ai/ha-mcp/pull/1405))
- Cache YAML instance to prevent CPU spikes in bulk edits ([#&#8203;1370](https://github.com/homeassistant-ai/ha-mcp/issues/1370))
  ([#&#8203;1371](https://github.com/homeassistant-ai/ha-mcp/pull/1371))
- **client**: Route get\_error\_log via hassio proxy on external-HAOS clients
  ([#&#8203;1360](https://github.com/homeassistant-ai/ha-mcp/pull/1360))
- Classify dashboard 404s ("unknown config specified") as RESOURCE\_NOT\_FOUND
  ([#&#8203;1345](https://github.com/homeassistant-ai/ha-mcp/pull/1345))
- Detect HA addon installs as http transport, not stdio ([#&#8203;1322](https://github.com/homeassistant-ai/ha-mcp/issues/1322))
  ([#&#8203;1327](https://github.com/homeassistant-ai/ha-mcp/pull/1327))
- Actionable 403 suggestion when addon has unmapped container ports ([#&#8203;1319](https://github.com/homeassistant-ai/ha-mcp/issues/1319))
  ([#&#8203;1325](https://github.com/homeassistant-ai/ha-mcp/pull/1325))
- Filter dismissed repairs in overview and system\_health ([#&#8203;1307](https://github.com/homeassistant-ai/ha-mcp/issues/1307))
  ([#&#8203;1309](https://github.com/homeassistant-ai/ha-mcp/pull/1309))
- Exit on HA container death + daily reset before CI check
  ([#&#8203;1295](https://github.com/homeassistant-ai/ha-mcp/pull/1295))
- Align ha\_config\_set\_dashboard with sibling re-fetch-after-save pattern ([#&#8203;1291](https://github.com/homeassistant-ai/ha-mcp/issues/1291))
  ([#&#8203;1301](https://github.com/homeassistant-ai/ha-mcp/pull/1301))
- Allow str.replace in python\_transform; hint at search mode on IndexError
  ([#&#8203;1287](https://github.com/homeassistant-ai/ha-mcp/pull/1287))
- **array\_patch**: Tighten validation and surface silent failures
  ([#&#8203;1285](https://github.com/homeassistant-ai/ha-mcp/pull/1285))
- HA Core proxy fallback for ha\_get\_logs(source=system\_service) on non-addon installs
  ([#&#8203;1283](https://github.com/homeassistant-ai/ha-mcp/pull/1283))

##### Performance Improvements

- Tighten \_poll\_for\_automation\_entity first-poll cadence
  ([#&#8203;1384](https://github.com/homeassistant-ai/ha-mcp/pull/1384))
- Parallelize ha\_get\_system\_health optional sections via asyncio.gather
  ([#&#8203;1336](https://github.com/homeassistant-ai/ha-mcp/pull/1336))

##### Refactoring

- **service**: Compact ha\_call\_service result default ([#&#8203;1446](https://github.com/homeassistant-ai/ha-mcp/issues/1446))
  ([#&#8203;1447](https://github.com/homeassistant-ai/ha-mcp/pull/1447))
- Rename ha\_update\_device → ha\_set\_device
  ([#&#8203;1444](https://github.com/homeassistant-ai/ha-mcp/pull/1444))
- Remove duplicate flat area/floor list tools (consolidation followup to [#&#8203;1016](https://github.com/homeassistant-ai/ha-mcp/issues/1016))
  ([#&#8203;1429](https://github.com/homeassistant-ai/ha-mcp/pull/1429))
- **complexity**: Migrate tools\_utility.py to class-based pattern
  ([#&#8203;1423](https://github.com/homeassistant-ai/ha-mcp/pull/1423))
- **complexity**: Reduce C901 violations in tools/ — batch 4
  ([#&#8203;1408](https://github.com/homeassistant-ai/ha-mcp/pull/1408))
- Route \_poll\_for\_automation\_entity through WS event waiter (closes [#&#8203;1395](https://github.com/homeassistant-ai/ha-mcp/issues/1395))
  ([#&#8203;1406](https://github.com/homeassistant-ai/ha-mcp/pull/1406))
- **yaml**: Use threading.local subclass for cached instance
  ([#&#8203;1396](https://github.com/homeassistant-ai/ha-mcp/pull/1396))
- Align dashboards 404 shape with sibling config tools
  ([#&#8203;1386](https://github.com/homeassistant-ai/ha-mcp/pull/1386))
- Complete singular warning → warnings list migration repo-wide (closes [#&#8203;1332](https://github.com/homeassistant-ai/ha-mcp/issues/1332))
  ([#&#8203;1341](https://github.com/homeassistant-ai/ha-mcp/pull/1341))
- Complete warnings-list migration for lifecycle-write tools
  ([#&#8203;1340](https://github.com/homeassistant-ai/ha-mcp/pull/1340))
- Drop redundant identifier echo key from ha\_config\_get\_automation
  ([#&#8203;1354](https://github.com/homeassistant-ai/ha-mcp/pull/1354))
- Drop logger.error in config-tool except blocks ([#&#8203;1302](https://github.com/homeassistant-ai/ha-mcp/issues/1302))
  ([#&#8203;1353](https://github.com/homeassistant-ai/ha-mcp/pull/1353))
- Extend validate\_identifier\_not\_empty to automations/scripts/dashboards CRUD (closes [#&#8203;1313](https://github.com/homeassistant-ai/ha-mcp/issues/1313))
  ([#&#8203;1321](https://github.com/homeassistant-ai/ha-mcp/pull/1321))
- Migrate tools\_config\_scenes inline empty-id guards to shared helper
  ([#&#8203;1320](https://github.com/homeassistant-ai/ha-mcp/pull/1320))
- Remove ha\_get\_helper\_schema (closes [#&#8203;1186](https://github.com/homeassistant-ai/ha-mcp/issues/1186))
  ([#&#8203;1315](https://github.com/homeassistant-ai/ha-mcp/pull/1315))
- Consolidate skill tools; fix stable submodule packaging
  ([#&#8203;1289](https://github.com/homeassistant-ai/ha-mcp/pull/1289))
- Align tools\_config\_automations.py error-handling with sibling pattern ([#&#8203;1290](https://github.com/homeassistant-ai/ha-mcp/issues/1290))
  ([#&#8203;1298](https://github.com/homeassistant-ai/ha-mcp/pull/1298))

***

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

##### Fixed

- **ci**: Install libguestfs in HAOS publish workflow
  ([#&#8203;1358](https://github.com/homeassistant-ai/ha-mcp/pull/1358))

##### Build System

- **deps**: Bump esbuild from 0.24.2 to 0.25.0 in /tests/js
  ([#&#8203;1427](https://github.com/homeassistant-ai/ha-mcp/pull/1427))
- **deps**: Bump devalue from 5.6.4 to 5.8.1 in /site
  ([#&#8203;1282](https://github.com/homeassistant-ai/ha-mcp/pull/1282))
- **deps**: Bump astro from 6.1.6 to 6.1.10 in /site
  ([#&#8203;1274](https://github.com/homeassistant-ai/ha-mcp/pull/1274))

##### Chores

- **addon**: Publish dev addon version 7.5.0.dev360 \[skip ci]
  ([`ad7aed1`](https://github.com/homeassistant-ai/ha-mcp/commit/ad7aed13d6843967e2a259ae46fec2bbc6abc896))
- Sync tool docs after merge \[skip ci]
  ([`9c4984f`](https://github.com/homeassistant-ai/ha-mcp/commit/9c4984fc44d30c15a96372fbc1b401fbd679dc8f))
- **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.5.4
  ([#&#8203;1450](https://github.com/homeassistant-ai/ha-mcp/pull/1450))
- **addon**: Publish dev addon version 7.5.0.dev359 \[skip ci]
  ([`b82d4ee`](https://github.com/homeassistant-ai/ha-mcp/commit/b82d4eea85792b620368d4859e99c143f9fbfadf))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.16
  ([#&#8203;1449](https://github.com/homeassistant-ai/ha-mcp/pull/1449))
- **addon**: Publish dev addon version 7.5.0.dev358 \[skip ci]
  ([`53fba6d`](https://github.com/homeassistant-ai/ha-mcp/commit/53fba6da76ce8c78a0de035883233a36762601bc))
- Sync tool docs after merge \[skip ci]
  ([`dc7750d`](https://github.com/homeassistant-ai/ha-mcp/commit/dc7750de16b60f70e40e6e5c8908d9e84e544683))
- **addon**: Publish dev addon version 7.5.0.dev357 \[skip ci]
  ([`fd150c9`](https://github.com/homeassistant-ai/ha-mcp/commit/fd150c9b9c4bdb9026e67eae340226c36b270d94))
- **addon**: Publish dev addon version 7.5.0.dev356 \[skip ci]
  ([`5fa1463`](https://github.com/homeassistant-ai/ha-mcp/commit/5fa14630229ba0d6b97d9328768e247bf9c20af4))
- **addon**: Publish dev addon version 7.5.0.dev355 \[skip ci]
  ([`174ac5d`](https://github.com/homeassistant-ai/ha-mcp/commit/174ac5dca7667702cd664420c50e0101366e6c3a))
- **addon**: Publish dev addon version 7.5.0.dev354 \[skip ci]
  ([`4f93989`](https://github.com/homeassistant-ai/ha-mcp/commit/4f939892dea2d1951541b1a557c51e957f6751c0))
- **addon**: Publish dev addon version 7.5.0.dev353 \[skip ci]
  ([`53f282f`](https://github.com/homeassistant-ai/ha-mcp/commit/53f282f19ee736637106f5baececa3939393778e))
- **addon**: Publish dev addon version 7.5.0.dev352 \[skip ci]
  ([`4911d46`](https://github.com/homeassistant-ai/ha-mcp/commit/4911d4682c4a865a93a8e821bc86bd26cef0ed5a))
- Sync tool docs after merge \[skip ci]
  ([`8a79837`](https://github.com/homeassistant-ai/ha-mcp/commit/8a798373a7b2f1adb94b93e65d626ab582c7bd5d))
- **addon**: Publish dev addon version 7.5.0.dev351 \[skip ci]
  ([`13afa9d`](https://github.com/homeassistant-ai/ha-mcp/commit/13afa9df026dd27332277140c34f7d41d0229efd))
- **addon**: Publish dev addon version 7.5.0.dev350 \[skip ci]
  ([`e93d680`](https://github.com/homeassistant-ai/ha-mcp/commit/e93d6806025c6d7fd2ef94c509a607c4ac85c8e0))
- **addon**: Publish dev addon version 7.5.0.dev349 \[skip ci]
  ([`1631ad1`](https://github.com/homeassistant-ai/ha-mcp/commit/1631ad10bdada4487d7b54049e92df6ddb29439a))
- **addon**: Publish dev addon version 7.5.0.dev348 \[skip ci]
  ([`18a8aef`](https://github.com/homeassistant-ai/ha-mcp/commit/18a8aef50675c1a881b279666e0af1b093679850))
- Sync tool docs after merge \[skip ci]
  ([`9e0493d`](https://github.com/homeassistant-ai/ha-mcp/commit/9e0493d248e23a8042841e90847662926395a448))
- **addon**: Publish dev addon version 7.5.0.dev347 \[skip ci]
  ([`e2067e4`](https://github.com/homeassistant-ai/ha-mcp/commit/e2067e446b127097e61d07516ae0e742ae2de6c8))
- Sync tool docs after merge \[skip ci]
  ([`7bdb3d4`](https://github.com/homeassistant-ai/ha-mcp/commit/7bdb3d402b60900a1012b8269e52b7cbeb400f84))
- **addon**: Publish dev addon version 7.5.0.dev346 \[skip ci]
  ([`c2d4dd7`](https://github.com/homeassistant-ai/ha-mcp/commit/c2d4dd7091653a618326f665fd34b5458d26c8f6))
- Sync tool docs after merge \[skip ci]
  ([`393b354`](https://github.com/homeassistant-ai/ha-mcp/commit/393b354e8a8e1280029eee9521971c693526970a))
- **addon**: Publish dev addon version 7.5.0.dev345 \[skip ci]
  ([`42ede8b`](https://github.com/homeassistant-ai/ha-mcp/commit/42ede8b0e6066faf3ed6f6920629a8d94d77f69a))
- **addon**: Publish dev addon version 7.5.0.dev344 \[skip ci]
  ([`e6cc7a1`](https://github.com/homeassistant-ai/ha-mcp/commit/e6cc7a1b8ed252f1aaa2b8c964e56c97a27b94c2))
- Sync tool docs after merge \[skip ci]
  ([`fb35f30`](https://github.com/homeassistant-ai/ha-mcp/commit/fb35f305cbf7de6807c29bd3ada9da2f028ff730))
- **addon**: Publish dev addon version 7.5.0.dev343 \[skip ci]
  ([`401b7b4`](https://github.com/homeassistant-ai/ha-mcp/commit/401b7b4d707dbb3c56a3afecad9e9d22ecbc7d1e))
- **addon**: Publish dev addon version 7.5.0.dev342 \[skip ci]
  ([`0679371`](https://github.com/homeassistant-ai/ha-mcp/commit/06793710b04a985ed0d66861266b9b79494e4111))
- Sync tool docs after merge \[skip ci]
  ([`f6796ec`](https://github.com/homeassistant-ai/ha-mcp/commit/f6796ec19b4596455ff54b58746bd3e075d5d280))
- **addon**: Publish dev addon version 7.5.0.dev341 \[skip ci]
  ([`3654478`](https://github.com/homeassistant-ai/ha-mcp/commit/3654478f80428d15671e49162acecea19d67f672))
- **addon**: Publish dev addon version 7.5.0.dev340 \[skip ci]
  ([`64f00b6`](https://github.com/homeassistant-ai/ha-mcp/commit/64f00b658846cad0e0714f37d221fb65327015cf))
- **addon**: Publish dev addon version 7.5.0.dev339 \[skip ci]
  ([`d6e8873`](https://github.com/homeassistant-ai/ha-mcp/commit/d6e88731556464deb768524dc24c90fd0a622a96))
- Sync tool docs after merge \[skip ci]
  ([`7525e93`](https://github.com/homeassistant-ai/ha-mcp/commit/7525e930a872871fed78f9b1461aea8e78eb5ad7))
- **addon**: Publish dev addon version 7.5.0.dev338 \[skip ci]
  ([`288ca4a`](https://github.com/homeassistant-ai/ha-mcp/commit/288ca4abd00b2b366cbaf671c1ace3398cc8f2fd))
- **addon**: Publish dev addon version 7.5.0.dev337 \[skip ci]
  ([`f539ae5`](https://github.com/homeassistant-ai/ha-mcp/commit/f539ae5aa881853d831e6952ff3af8bc85eb8b2a))
- **addon**: Publish dev addon version 7.5.0.dev336 \[skip ci]
  ([`5568a86`](https://github.com/homeassistant-ai/ha-mcp/commit/5568a861002cfb909e29368bd4e93ef0bd2e4c03))
- **addon**: Publish dev addon version 7.5.0.dev335 \[skip ci]
  ([`e069405`](https://github.com/homeassistant-ai/ha-mcp/commit/e0694054bd8627f6464379c34c1e3eec5b080a44))
- **addon**: Publish dev addon version 7.5.0.dev334 \[skip ci]
  ([`f7be6ea`](https://github.com/homeassistant-ai/ha-mcp/commit/f7be6ead486a32840a935a1e5ec78a34f8003c0d))
- **addon**: Publish dev addon version 7.5.0.dev333 \[skip ci]
  ([`cb480ea`](https://github.com/homeassistant-ai/ha-mcp/commit/cb480eae4d855345c19a6465351e0bdef2bdf5c3))
- Sync tool docs after merge \[skip ci]
  ([`9a5bc3c`](https://github.com/homeassistant-ai/ha-mcp/commit/9a5bc3c61372159bb936d7b929166924343dc76f))
- **addon**: Publish dev addon version 7.5.0.dev332 \[skip ci]
  ([`e0e59ee`](https://github.com/homeassistant-ai/ha-mcp/commit/e0e59ee08072bb4aac51e90928fab2d68f6156ce))
- Sync tool docs after merge \[skip ci]
  ([`499ebf0`](https://github.com/homeassistant-ai/ha-mcp/commit/499ebf0c9d4c4ec3784dd72782fe301cd9c19d60))
- **addon**: Publish dev addon version 7.5.0.dev331 \[skip ci]
  ([`3e0ce92`](https://github.com/homeassistant-ai/ha-mcp/commit/3e0ce92e11777a8fd9c86153e4c248007b8edae9))
- **addon**: Publish dev addon version 7.5.0.dev330 \[skip ci]
  ([`e48d056`](https://github.com/homeassistant-ai/ha-mcp/commit/e48d056ab356e11f8b2952b6c675307d1e76a895))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.15
  ([#&#8203;1376](https://github.com/homeassistant-ai/ha-mcp/pull/1376))
- **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.5.3
  ([#&#8203;1377](https://github.com/homeassistant-ai/ha-mcp/pull/1377))
- **addon**: Publish dev addon version 7.5.0.dev329 \[skip ci]
  ([`c523c50`](https://github.com/homeassistant-ai/ha-mcp/commit/c523c502c5c6f282905620d3db489e9c69ec2472))
- Sync tool docs after merge \[skip ci]
  ([`5b7a8aa`](https://github.com/homeassistant-ai/ha-mcp/commit/5b7a8aa0794359a90d7b89a9cc61d73d8cf038d2))
- **addon**: Publish dev addon version 7.5.0.dev328 \[skip ci]
  ([`6c42fba`](https://github.com/homeassistant-ai/ha-mcp/commit/6c42fba92d4786d7498f738c53dfe50c899d0890))
- **addon**: Publish dev addon version 7.5.0.dev327 \[skip ci]
  ([`aecd025`](https://github.com/homeassistant-ai/ha-mcp/commit/aecd025eeea3d1edcd9466db3720cb17de71b90a))
- **addon**: Publish dev addon version 7.5.0.dev326 \[skip ci]
  ([`399c17c`](https://github.com/homeassistant-ai/ha-mcp/commit/399c17c36602a7767491df6e317186cb6291f3e4))
- **addon**: Publish dev addon version 7.5.0.dev325 \[skip ci]
  ([`b580b45`](https://github.com/homeassistant-ai/ha-mcp/commit/b580b452e9007deb33acc49098238988aa768603))
- Sync tool docs after merge \[skip ci]
  ([`8567c3a`](https://github.com/homeassistant-ai/ha-mcp/commit/8567c3a6e66e7bebbbad8e96b94058f8456deb14))
- **addon**: Publish dev addon version 7.5.0.dev324 \[skip ci]
  ([`a65579d`](https://github.com/homeassistant-ai/ha-mcp/commit/a65579d0b6e4a8bb7f8c1ed97fd432cd770c47a4))
- **addon**: Publish dev addon version 7.5.0.dev323 \[skip ci]
  ([`c7667ba`](https://github.com/homeassistant-ai/ha-mcp/commit/c7667ba66538f1ba8a06b7616fb15896bf021744))
- **addon**: Publish dev addon version 7.5.0.dev322 \[skip ci]
  ([`44d15a8`](https://github.com/homeassistant-ai/ha-mcp/commit/44d15a89cbacc2c38ab39868b380414507a6708b))
- **addon**: Publish dev addon version 7.5.0.dev321 \[skip ci]
  ([`8739f6c`](https://github.com/homeassistant-ai/ha-mcp/commit/8739f6c09121c88a596470df293ac8e7e0f1050a))
- **addon**: Publish dev addon version 7.5.0.dev320 \[skip ci]
  ([`02b6e47`](https://github.com/homeassistant-ai/ha-mcp/commit/02b6e47f8eb6c778f709d7adca115638c2fe98a2))
- Sync tool docs after merge \[skip ci]
  ([`ab68c9a`](https://github.com/homeassistant-ai/ha-mcp/commit/ab68c9a9aa94ac6861af7b59997ec679f1849503))
- **addon**: Publish dev addon version 7.5.0.dev319 \[skip ci]
  ([`4472904`](https://github.com/homeassistant-ai/ha-mcp/commit/44729040c11af0bd97189080c1ec59995fc563b4))
- **addon**: Publish dev addon version 7.5.0.dev318 \[skip ci]
  ([`e030dbc`](https://github.com/homeassistant-ai/ha-mcp/commit/e030dbcc99e002751834274b1d53da161ab27759))
- **addon**: Publish dev addon version 7.5.0.dev317 \[skip ci]
  ([`d87855c`](https://github.com/homeassistant-ai/ha-mcp/commit/d87855cf07cef9d9d7babf0bbec74d228149327d))
- Sync tool docs after merge \[skip ci]
  ([`a72a4e8`](https://github.com/homeassistant-ai/ha-mcp/commit/a72a4e8c0de99a8516f3820545642c37a31c17e5))
- **addon**: Publish dev addon version 7.5.0.dev316 \[skip ci]
  ([`bd9397f`](https://github.com/homeassistant-ai/ha-mcp/commit/bd9397f3a088a24a14af66a97afcf1c97904ef61))
- **addon**: Publish dev addon version 7.5.0.dev315 \[skip ci]
  ([`264bfc2`](https://github.com/homeassistant-ai/ha-mcp/commit/264bfc2fcb5ef6fe3ee6f9e073b69e0469eeafdd))
- **addon**: Publish dev addon version 7.5.0.dev314 \[skip ci]
  ([`df62881`](https://github.com/homeassistant-ai/ha-mcp/commit/df6288194cdde8b625132f19b0dd3edb2142a2b3))
- **addon**: Publish dev addon version 7.5.0.dev313 \[skip ci]
  ([`f6c47ca`](https://github.com/homeassistant-ai/ha-mcp/commit/f6c47caa21b0d82799b2be9330f97aa5494aa3c8))
- **addon**: Publish dev addon version 7.5.0.dev312 \[skip ci]
  ([`2bb7a74`](https://github.com/homeassistant-ai/ha-mcp/commit/2bb7a74f06e0652a729a735b08fc7cdc20a034f6))
- Sync tool docs after merge \[skip ci]
  ([`137e279`](https://github.com/homeassistant-ai/ha-mcp/commit/137e27943b1b704fe1d3e3a5b525b4d82bce33eb))
- **addon**: Publish dev addon version 7.5.0.dev311 \[skip ci]
  ([`28324ea`](https://github.com/homeassistant-ai/ha-mcp/commit/28324ea4a92fbf61f5ee40c561286c466af2309a))
- Sync tool docs after merge \[skip ci]
  ([`9a753d4`](https://github.com/homeassistant-ai/ha-mcp/commit/9a753d49abb4ccd84e874451dbcc563260f9e19d))
- **addon**: Publish dev addon version 7.5.0.dev310 \[skip ci]
  ([`f893b2e`](https://github.com/homeassistant-ai/ha-mcp/commit/f893b2ebf91967d0e2548f9205a15bfe0e7d86c2))
- **addon**: Publish dev addon version 7.5.0.dev309 \[skip ci]
  ([`8cbdb7b`](https://github.com/homeassistant-ai/ha-mcp/commit/8cbdb7bf318707fed1a0b3e8cb9922e6c170521e))
- **addon**: Publish dev addon version 7.5.0.dev308 \[skip ci]
  ([`2d18016`](https://github.com/homeassistant-ai/ha-mcp/commit/2d18016de8eb26a18cfb471e7b5f755f1309bd36))
- **addon**: Publish dev addon version 7.5.0.dev307 \[skip ci]
  ([`3fc3b28`](https://github.com/homeassistant-ai/ha-mcp/commit/3fc3b28881f237df02da8b65467fba9a7d693009))
- Sync tool docs after merge \[skip ci]
  ([`9e6cff8`](https://github.com/homeassistant-ai/ha-mcp/commit/9e6cff8f641bcd8122e94fc8be9c30aa18456e80))
- **addon**: Publish dev addon version 7.5.0.dev306 \[skip ci]
  ([`8bdd0fc`](https://github.com/homeassistant-ai/ha-mcp/commit/8bdd0fca9c107f45e4c3719e7616984b68876a9e))
- **addon**: Publish dev addon version 7.5.0.dev305 \[skip ci]
  ([`83535b9`](https://github.com/homeassistant-ai/ha-mcp/commit/83535b99e190fb56b7ce227334331ebf2affb3e1))
- **addon**: Publish dev addon version 7.5.0.dev304 \[skip ci]
  ([`1435b3a`](https://github.com/homeassistant-ai/ha-mcp/commit/1435b3a24d73503e189b83f8ea5aaeea20d9ebf3))
- **addon**: Publish dev addon version 7.5.0.dev303 \[skip ci]
  ([`e2da659`](https://github.com/homeassistant-ai/ha-mcp/commit/e2da6591d6ad2def0b48a9be750cb8a41f40524d))
- Sync tool docs after merge \[skip ci]
  ([`23789fa`](https://github.com/homeassistant-ai/ha-mcp/commit/23789fa978591b7d3966894082a2683ca6a6ae3b))
- **addon**: Publish dev addon version 7.5.0.dev302 \[skip ci]
  ([`6c8e574`](https://github.com/homeassistant-ai/ha-mcp/commit/6c8e574e7e3d8d78b3a2e48f6a70531d7a95eed8))
- Sync tool docs after merge \[skip ci]
  ([`d2329cb`](https://github.com/homeassistant-ai/ha-mcp/commit/d2329cbff39891d209317528fd7b42759c3414a8))
- **addon**: Publish dev addon version 7.5.0.dev301 \[skip ci]
  ([`bb538f7`](https://github.com/homeassistant-ai/ha-mcp/commit/bb538f70d2fc57072f09cfc6202e9d3dc1a2d257))
- Sync tool docs after merge \[skip ci]
  ([`f70f0e1`](https://github.com/homeassistant-ai/ha-mcp/commit/f70f0e14222b29a8923e706f113d1fafb4a5c23e))
- **addon**: Publish version 7.5.0 \[skip ci]
  ([`9c5eb37`](https://github.com/homeassistant-ai/ha-mcp/commit/9c5eb37779236ef19366a2a59a667d3916458e5a))

##### Continuous Integration

- **deps**: Bump actions/upload-artifact in the github-actions group
  ([#&#8203;1437](https://github.com/homeassistant-ai/ha-mcp/pull/1437))
- Share qcow2 cache + GHCR fallback between HAOS lanes
  ([#&#8203;1407](https://github.com/homeassistant-ai/ha-mcp/pull/1407))
- Add ruff format --check on changed Python files
  ([#&#8203;1387](https://github.com/homeassistant-ai/ha-mcp/pull/1387))
- Exempt assigned issues from stale bot
  ([#&#8203;1368](https://github.com/homeassistant-ai/ha-mcp/pull/1368))
- **deps**: Bump the github-actions group with 3 updates
  ([#&#8203;1362](https://github.com/homeassistant-ai/ha-mcp/pull/1362))

##### Refactoring

- Consolidate lovelace/dashboards/list through shared helper
  ([#&#8203;1344](https://github.com/homeassistant-ai/ha-mcp/pull/1344))

##### Testing

- **haos-e2e**: Bake + install webhook-proxy addon and exercise its runtime
  ([#&#8203;1443](https://github.com/homeassistant-ai/ha-mcp/pull/1443))
- **config-subentry**: Mark forecast\_solar e2e as known flaky + relative-import sweep
  ([#&#8203;1430](https://github.com/homeassistant-ai/ha-mcp/pull/1430))
- **haos-e2e**: Trim cache-save race, compress GHCR qcow2, eval boot snapshot
  ([#&#8203;1428](https://github.com/homeassistant-ai/ha-mcp/pull/1428))
- JSDOM behaviour harness + auto-discovery parse coverage for every rendered <script>
  ([#&#8203;1425](https://github.com/homeassistant-ai/ha-mcp/pull/1425))
- **hacs**: Retry TestMcpToolsInstallation on flake
  ([#&#8203;1426](https://github.com/homeassistant-ai/ha-mcp/pull/1426))
- **e2e**: Drop redundant lifecycle roundtrips, keep only Matter Server ([#&#8203;1414](https://github.com/homeassistant-ai/ha-mcp/issues/1414))
  ([#&#8203;1419](https://github.com/homeassistant-ai/ha-mcp/pull/1419))
- **e2e**: Assert backend dispatch matches workflow env on every lane
  ([#&#8203;1409](https://github.com/homeassistant-ai/ha-mcp/pull/1409))
- Escape ideographic space and format file ([#&#8203;1237](https://github.com/homeassistant-ai/ha-mcp/issues/1237))
  ([#&#8203;1410](https://github.com/homeassistant-ai/ha-mcp/pull/1410))
- **e2e**: Measure \_POLL\_CADENCE p50/p99 to validate or retune (closes [#&#8203;1389](https://github.com/homeassistant-ai/ha-mcp/issues/1389))
  ([#&#8203;1398](https://github.com/homeassistant-ai/ha-mcp/pull/1398))
- **e2e**: Wait for addon state=started in haos proxy header test
  ([#&#8203;1402](https://github.com/homeassistant-ai/ha-mcp/pull/1402))
- Pin remaining \_classify\_by\_message branches
  ([#&#8203;1385](https://github.com/homeassistant-ai/ha-mcp/pull/1385))
- **haos-e2e**: Slim addon set + real-addon ha\_manage\_addon coverage (closes [#&#8203;1350](https://github.com/homeassistant-ai/ha-mcp/issues/1350))
  ([#&#8203;1379](https://github.com/homeassistant-ai/ha-mcp/pull/1379))
- **haos-e2e**: Close out [#&#8203;1349](https://github.com/homeassistant-ai/ha-mcp/issues/1349) — lifecycle, integrations, supervisor\_mock migration, no more skips
  ([#&#8203;1375](https://github.com/homeassistant-ai/ha-mcp/pull/1375))
- **e2e**: Consolidate readiness gates onto /api/core/state (refs [#&#8203;366](https://github.com/homeassistant-ai/ha-mcp/issues/366))
  ([#&#8203;1372](https://github.com/homeassistant-ai/ha-mcp/pull/1372))
- **e2e**: Tighten 5 readiness-gate budgets with 2-63x headroom (refs [#&#8203;366](https://github.com/homeassistant-ai/ha-mcp/issues/366))
  ([#&#8203;1369](https://github.com/homeassistant-ai/ha-mcp/pull/1369))
- Scaffold HAOS E2E tier image-build pipeline (refs [#&#8203;1281](https://github.com/homeassistant-ai/ha-mcp/issues/1281))
  ([#&#8203;1326](https://github.com/homeassistant-ai/ha-mcp/pull/1326))
- **e2e**: Instrument HA\_MCP\_TOOLS\_WAIT readiness gate (refs [#&#8203;366](https://github.com/homeassistant-ai/ha-mcp/issues/366))
  ([#&#8203;1346](https://github.com/homeassistant-ai/ha-mcp/pull/1346))
- **e2e**: Centralize wait\_for\_entity\_registration helper (refs [#&#8203;366](https://github.com/homeassistant-ai/ha-mcp/issues/366))
  ([#&#8203;1308](https://github.com/homeassistant-ai/ha-mcp/pull/1308))
- **e2e**: Unify dict-error message extraction across e2e tests (refs [#&#8203;366](https://github.com/homeassistant-ai/ha-mcp/issues/366))
  ([#&#8203;1311](https://github.com/homeassistant-ai/ha-mcp/pull/1311))
- **e2e**: Surface readiness-gate elapsed times in CI logs (refs [#&#8203;366](https://github.com/homeassistant-ai/ha-mcp/issues/366))
  ([#&#8203;1310](https://github.com/homeassistant-ai/ha-mcp/pull/1310))

</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/994
doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Jun 11, 2026
…➔ 7.7.0) (#243)

This PR contains the following updates:

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

---

### Release Notes

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

### [`v7.7.0`](https://github.com/homeassistant-ai/ha-mcp/blob/HEAD/CHANGELOG.md#v770-2026-06-10)

[Compare Source](homeassistant-ai/ha-mcp@v7.6.0...v7.7.0)

##### Added

- User-configurable custom filesystem directories for the file tools (closes [#&#8203;1567](homeassistant-ai/ha-mcp#1567))
  ([#&#8203;1568](homeassistant-ai/ha-mcp#1568))
- Expose all user-tunable env vars in the settings UI (add-on parity)
  ([#&#8203;1554](homeassistant-ai/ha-mcp#1554))
- Warn when default MCP\_SECRET\_PATH is bound non-loopback
  ([#&#8203;1472](homeassistant-ai/ha-mcp#1472))
- **search**: Consolidate ha\_search\_entities + ha\_deep\_search into ha\_search
  ([#&#8203;1529](homeassistant-ai/ha-mcp#1529))
- **addon**: Install the MCP Server add-on from the ha\_mcp\_tools integration
  ([#&#8203;1528](homeassistant-ai/ha-mcp#1528))
- Add opt-in dashboard screenshot mode
  ([#&#8203;1510](homeassistant-ai/ha-mcp#1510))
- Add Linux support for install & docs
  ([#&#8203;1096](homeassistant-ai/ha-mcp#1096))
- Surface the web settings page for non-add-on installs ([#&#8203;1458](homeassistant-ai/ha-mcp#1458))
  ([#&#8203;1511](homeassistant-ai/ha-mcp#1511))
- Convert Pydantic arg-validation errors to actionable ToolErrors
  ([#&#8203;1491](homeassistant-ai/ha-mcp#1491))
- Per-key toggles for automation/script/scene in packages/\*.yaml
  ([#&#8203;1476](homeassistant-ai/ha-mcp#1476))
- Direct skills retrieval for write tools + improved  best practice checker warnings with embedded skills responses ([#&#8203;1182](homeassistant-ai/ha-mcp#1182))
  ([#&#8203;1448](homeassistant-ai/ha-mcp#1448))
- Detect last\_changed/last\_updated duration math and suggest for: field ([#&#8203;1157](homeassistant-ai/ha-mcp#1157))
  ([#&#8203;1264](homeassistant-ai/ha-mcp#1264))
- Advanced settings panel + nested beta master toggle ([#&#8203;1164](homeassistant-ai/ha-mcp#1164))
  ([#&#8203;1431](homeassistant-ai/ha-mcp#1431))
- Allow automation/script/scene yaml\_path in packages/\*.yaml only
  ([#&#8203;1452](homeassistant-ai/ha-mcp#1452))
- Restrict ha\_mcp\_tools services to ha-mcp callers (caller token + ha\_call\_service refusal)
  ([#&#8203;1459](homeassistant-ai/ha-mcp#1459))

##### Changed

- **security**: Soften vulnerability-response SLA to best-effort
  ([`a24e7da`](homeassistant-ai/ha-mcp@a24e7da))
- Update contributors list \[contributors-updated]
  ([`7cc187f`](homeassistant-ai/ha-mcp@7cc187f))
- Telemetry wording — follow HA analytics setting, not opt-in-only
  ([#&#8203;1481](homeassistant-ai/ha-mcp#1481))
- **security**: Note that security-advisory disposition is API-blind (UI-only)
  ([#&#8203;1561](homeassistant-ai/ha-mcp#1561))
- **tools**: Sharpen ha\_eval\_template usage routing for compute-from-state queries
  ([#&#8203;1550](homeassistant-ai/ha-mcp#1550))
- Differentiate ha-mcp from Home Assistant's built-in MCP Server
  ([#&#8203;1542](homeassistant-ai/ha-mcp#1542))
- Update advanced mode notes
  ([#&#8203;1533](homeassistant-ai/ha-mcp#1533))
- **[#&#8203;1157](homeassistant-ai/ha-mcp#1157: Scripts native-for: guidance + fix numeric\_state-condition for: overclaim
  ([#&#8203;1480](homeassistant-ai/ha-mcp#1480))
- Clarify add-on vs uvx; add Codex + HTTP-native client setup
  ([#&#8203;1478](homeassistant-ai/ha-mcp#1478))
- **security**: Explicit threat model for trusted clients, LAN, sandbox, and OAuth tokens
  ([#&#8203;1463](homeassistant-ai/ha-mcp#1463))
- Clarify telemetry is a planned future feature, not implemented
  ([#&#8203;1469](homeassistant-ai/ha-mcp#1469))

##### Fixed

- **tools**: Bring HA API field names to 2026.6 — stale docstrings, automation plural canonicalization, fan speed (closes [#&#8203;1540](homeassistant-ai/ha-mcp#1540))
  ([#&#8203;1566](homeassistant-ai/ha-mcp#1566))
- **energy**: Accept "water" energy source in ha\_manage\_energy\_prefs
  ([#&#8203;1553](homeassistant-ai/ha-mcp#1553))
- **tools**: Use action: not service: in automation docstring examples
  ([#&#8203;1539](homeassistant-ai/ha-mcp#1539))
- **security**: Narrow GHSA-mc92-ww4q-6fg4 to the masker and log redaction
  ([#&#8203;1512](homeassistant-ai/ha-mcp#1512))
- **addon**: Restrict settings UI root routes to HA ingress
  ([#&#8203;1508](homeassistant-ai/ha-mcp#1508))
- Add name attributes to generated settings-UI form controls (a11y)
  ([#&#8203;1497](homeassistant-ai/ha-mcp#1497))
- Add-on-aware code-mode locked note + suppress settings-UI favicon 404
  ([#&#8203;1494](homeassistant-ai/ha-mcp#1494))
- Surface the real reason a WebSocket connection failed
  ([#&#8203;1495](homeassistant-ai/ha-mcp#1495))
- Remove str from ha\_bulk\_control.operations schema + fix wrong-reason test
  ([#&#8203;1492](homeassistant-ai/ha-mcp#1492))
- Remove str from bool/int param schemas across all tools
  ([#&#8203;1490](homeassistant-ai/ha-mcp#1490))
- **addon**: Expose non-beta tool options on the stable add-on
  ([#&#8203;1488](homeassistant-ai/ha-mcp#1488))
- **addon**: Enable ingress so the stable add-on shows the Open Web UI / Settings UI
  ([#&#8203;1486](homeassistant-ai/ha-mcp#1486))
- Refine last\_changed/last\_updated duration-math detector ([#&#8203;1157](homeassistant-ai/ha-mcp#1157))
  ([#&#8203;1483](homeassistant-ai/ha-mcp#1483))
- Remove str from config param schema on service and entity tools
  ([#&#8203;1487](homeassistant-ai/ha-mcp#1487))
- Surface flow-helper config to agents reading UI-created templates
  ([#&#8203;1474](homeassistant-ai/ha-mcp#1474))
- Remove str from config param schema on set tools
  ([#&#8203;1485](homeassistant-ai/ha-mcp#1485))
- Route malformed ha\_mcp\_tools version to a distinct reinstall error
  ([#&#8203;1484](homeassistant-ai/ha-mcp#1484))
- Persist DCR client registrations and HMAC secret across restarts ([#&#8203;1261](homeassistant-ai/ha-mcp#1261))
  ([#&#8203;1265](homeassistant-ai/ha-mcp#1265))
- Stop dev builds from publishing the :latest Docker tag
  ([#&#8203;1477](homeassistant-ai/ha-mcp#1477))
- Subscribe to HACS dispatch signal instead of 10x1s blind poll
  ([#&#8203;1455](homeassistant-ai/ha-mcp#1455))
- Reject python\_transform while loops
  ([#&#8203;1462](homeassistant-ai/ha-mcp#1462))

##### Performance Improvements

- Fast-fail HACS not-found lookups + batch-verify deep-search E2E fixtures ([#&#8203;1515](homeassistant-ai/ha-mcp#1515))
  ([#&#8203;1552](homeassistant-ai/ha-mcp#1552))

##### Refactoring

- Add display title to ha\_get\_skill\_guide tool
  ([#&#8203;1543](homeassistant-ai/ha-mcp#1543))
- Fold ha\_check\_config into ha\_get\_system\_health include="config\_check"
  ([#&#8203;1516](homeassistant-ai/ha-mcp#1516))
- **c901**: Smart\_search.py below C901 threshold
  ([#&#8203;1507](homeassistant-ai/ha-mcp#1507))
- **hacs**: Consolidate HACS tools into ha\_get\_hacs + ha\_manage\_hacs ([#&#8203;1045](homeassistant-ai/ha-mcp#1045))
  ([#&#8203;1502](homeassistant-ai/ha-mcp#1502))
- **c901**: Tools\_config\_helpers.py below C901 threshold
  ([#&#8203;1498](homeassistant-ai/ha-mcp#1498))
- **complexity**: Reduce C901 in tools\_addons.py via class-based pattern
  ([#&#8203;1432](homeassistant-ai/ha-mcp#1432))

***

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

##### Changed

- Trim AGENTS.md below 40k + improve subdirectory CLAUDE.md files
  ([#&#8203;1499](homeassistant-ai/ha-mcp#1499))

##### Chores

- **addon**: Publish dev addon version 7.6.0.dev415 \[skip ci]
  ([`2c0148b`](homeassistant-ai/ha-mcp@2c0148b))
- **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.6.1
  ([#&#8203;1571](homeassistant-ai/ha-mcp#1571))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.19
  ([#&#8203;1570](homeassistant-ai/ha-mcp#1570))
- **addon**: Publish dev addon version 7.6.0.dev414 \[skip ci]
  ([`0bf9e39`](homeassistant-ai/ha-mcp@0bf9e39))
- Sync tool docs after merge \[skip ci]
  ([`8f5e037`](homeassistant-ai/ha-mcp@8f5e037))
- **addon**: Publish dev addon version 7.6.0.dev413 \[skip ci]
  ([`6729327`](homeassistant-ai/ha-mcp@6729327))
- Sync tool docs after merge \[skip ci]
  ([`4341e95`](homeassistant-ai/ha-mcp@4341e95))
- **addon**: Publish dev addon version 7.6.0.dev412 \[skip ci]
  ([`148f506`](homeassistant-ai/ha-mcp@148f506))
- **addon**: Publish dev addon version 7.6.0.dev411 \[skip ci]
  ([`287fe55`](homeassistant-ai/ha-mcp@287fe55))
- Sync tool docs after merge \[skip ci]
  ([`b642b8e`](homeassistant-ai/ha-mcp@b642b8e))
- **addon**: Publish dev addon version 7.6.0.dev410 \[skip ci]
  ([`29e34dd`](homeassistant-ai/ha-mcp@29e34dd))
- **addon**: Publish dev addon version 7.6.0.dev409 \[skip ci]
  ([`dec16a1`](homeassistant-ai/ha-mcp@dec16a1))
- Sync tool docs after merge \[skip ci]
  ([`dd72535`](homeassistant-ai/ha-mcp@dd72535))
- **addon**: Publish dev addon version 7.6.0.dev408 \[skip ci]
  ([`c3c89f1`](homeassistant-ai/ha-mcp@c3c89f1))
- **addon**: Publish dev addon version 7.6.0.dev407 \[skip ci]
  ([`94195ee`](homeassistant-ai/ha-mcp@94195ee))
- **addon**: Publish dev addon version 7.6.0.dev406 \[skip ci]
  ([`f439424`](homeassistant-ai/ha-mcp@f439424))
- Sync tool docs after merge \[skip ci]
  ([`7464277`](homeassistant-ai/ha-mcp@7464277))
- **addon**: Publish dev addon version 7.6.0.dev405 \[skip ci]
  ([`146eb07`](homeassistant-ai/ha-mcp@146eb07))
- **addon**: Publish dev addon version 7.6.0.dev404 \[skip ci]
  ([`6a707bb`](homeassistant-ai/ha-mcp@6a707bb))
- Sync tool docs after merge \[skip ci]
  ([`85f3935`](homeassistant-ai/ha-mcp@85f3935))
- **addon**: Publish dev addon version 7.6.0.dev403 \[skip ci]
  ([`be1fa1d`](homeassistant-ai/ha-mcp@be1fa1d))
- Sync tool docs after merge \[skip ci]
  ([`d9adbd2`](homeassistant-ai/ha-mcp@d9adbd2))
- **addon**: Publish dev addon version 7.6.0.dev402 \[skip ci]
  ([`41ad7ca`](homeassistant-ai/ha-mcp@41ad7ca))
- **addon**: Publish dev addon version 7.6.0.dev401 \[skip ci]
  ([`8205034`](homeassistant-ai/ha-mcp@8205034))
- **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.18
  ([#&#8203;1523](homeassistant-ai/ha-mcp#1523))
- **addon**: Publish dev addon version 7.6.0.dev400 \[skip ci]
  ([`532cdc4`](homeassistant-ai/ha-mcp@532cdc4))
- Sync tool docs after merge \[skip ci]
  ([`e2c08ff`](homeassistant-ai/ha-mcp@e2c08ff))
- **addon**: Publish dev addon version 7.6.0.dev399 \[skip ci]
  ([`9501348`](homeassistant-ai/ha-mcp@9501348))
- Sync tool docs after merge \[skip ci]
  ([`e70a050`](homeassistant-ai/ha-mcp@e70a050))
- **addon**: Publish dev addon version 7.6.0.dev398 \[skip ci]
  ([`0b61424`](homeassistant-ai/ha-mcp@0b61424))
- Sync tool docs after merge \[skip ci]
  ([`6df7987`](homeassistant-ai/ha-mcp@6df7987))
- **addon**: Publish dev addon version 7.6.0.dev397 \[skip ci]
  ([`c34915f`](homeassistant-ai/ha-mcp@c34915f))
- Sync tool docs after merge \[skip ci]
  ([`6f88e7e`](homeassistant-ai/ha-mcp@6f88e7e))
- **addon**: Publish dev addon version 7.6.0.dev396 \[skip ci]
  ([`1490e19`](homeassistant-ai/ha-mcp@1490e19))
- Sync tool docs after merge \[skip ci]
  ([`ac2c7d0`](homeassistant-ai/ha-mcp@ac2c7d0))
- **addon**: Publish dev addon version 7.6.0.dev395 \[skip ci]
  ([`11e5ee8`](homeassistant-ai/ha-mcp@11e5ee8))
- **addon**: Publish dev addon version 7.6.0.dev394 \[skip ci]
  ([`5fc3e0f`](homeassistant-ai/ha-mcp@5fc3e0f))
- Flag untrusted third-party content in HACS and add-on tool responses
  ([#&#8203;1509](homeassistant-ai/ha-mcp#1509))
- **addon**: Publish dev addon version 7.6.0.dev393 \[skip ci]
  ([`9a37c28`](homeassistant-ai/ha-mcp@9a37c28))
- **addon**: Publish dev addon version 7.6.0.dev392 \[skip ci]
  ([`4f737ac`](homeassistant-ai/ha-mcp@4f737ac))
- **addon**: Publish dev addon version 7.6.0.dev391 \[skip ci]
  ([`fd5d3f2`](homeassistant-ai/ha-mcp@fd5d3f2))
- **addon**: Publish dev addon version 7.6.0.dev390 \[skip ci]
  ([`28dedf4`](homeassistant-ai/ha-mcp@28dedf4))
- **addon**: Publish dev addon version 7.6.0.dev389 \[skip ci]
  ([`9443446`](homeassistant-ai/ha-mcp@9443446))
- Sync tool docs after merge \[skip ci]
  ([`94fcbeb`](homeassistant-ai/ha-mcp@94fcbeb))
- **addon**: Publish dev addon version 7.6.0.dev388 \[skip ci]
  ([`ccc1816`](homeassistant-ai/ha-mcp@ccc1816))
- Drop dead entity\_cache attr + ruff-format fuzzy\_search.py
  ([#&#8203;1503](homeassistant-ai/ha-mcp#1503))
- **addon**: Publish dev addon version 7.6.0.dev387 \[skip ci]
  ([`057c108`](homeassistant-ai/ha-mcp@057c108))
- **addon**: Publish dev addon version 7.6.0.dev386 \[skip ci]
  ([`66f4ac6`](homeassistant-ai/ha-mcp@66f4ac6))
- **addon**: Publish dev addon version 7.6.0.dev385 \[skip ci]
  ([`f331f11`](homeassistant-ai/ha-mcp@f331f11))
- **addon**: Publish dev addon version 7.6.0.dev384 \[skip ci]
  ([`17c319e`](homeassistant-ai/ha-mcp@17c319e))
- Sync tool docs after merge \[skip ci]
  ([`f36cbb7`](homeassistant-ai/ha-mcp@f36cbb7))
- **addon**: Publish dev addon version 7.6.0.dev383 \[skip ci]
  ([`782ba3b`](homeassistant-ai/ha-mcp@782ba3b))
- **addon**: Publish dev addon version 7.6.0.dev382 \[skip ci]
  ([`b647ab6`](homeassistant-ai/ha-mcp@b647ab6))
- **addon**: Publish dev addon version 7.6.0.dev381 \[skip ci]
  ([`e999c24`](homeassistant-ai/ha-mcp@e999c24))
- Sync tool docs after merge \[skip ci]
  ([`50d76b0`](homeassistant-ai/ha-mcp@50d76b0))
- **addon**: Publish dev addon version 7.6.0.dev380 \[skip ci]
  ([`ebdc69b`](homeassistant-ai/ha-mcp@ebdc69b))
- Sync tool docs after merge \[skip ci]
  ([`1f03967`](homeassistant-ai/ha-mcp@1f03967))
- **addon**: Publish dev addon version 7.6.0.dev379 \[skip ci]
  ([`7cabb2e`](homeassistant-ai/ha-mcp@7cabb2e))
- **addon**: Publish dev addon version 7.6.0.dev378 \[skip ci]
  ([`7b631c7`](homeassistant-ai/ha-mcp@7b631c7))
- **addon**: Publish dev addon version 7.6.0.dev377 \[skip ci]
  ([`ea7b614`](homeassistant-ai/ha-mcp@ea7b614))
- **addon**: Publish dev addon version 7.6.0.dev376 \[skip ci]
  ([`fd33ecd`](homeassistant-ai/ha-mcp@fd33ecd))
- Sync tool docs after merge \[skip ci]
  ([`ba8fae3`](homeassistant-ai/ha-mcp@ba8fae3))
- **addon**: Publish dev addon version 7.6.0.dev375 \[skip ci]
  ([`afcd0d8`](homeassistant-ai/ha-mcp@afcd0d8))
- Sync tool docs after merge \[skip ci]
  ([`f9b55ad`](homeassistant-ai/ha-mcp@f9b55ad))
- **addon**: Publish dev addon version 7.6.0.dev374 \[skip ci]
  ([`eecdf8b`](homeassistant-ai/ha-mcp@eecdf8b))
- Sync tool docs after merge \[skip ci]
  ([`ef16a8a`](homeassistant-ai/ha-mcp@ef16a8a))
- **addon**: Publish dev addon version 7.6.0.dev373 \[skip ci]
  ([`9a93703`](homeassistant-ai/ha-mcp@9a93703))
- **addon**: Publish dev addon version 7.6.0.dev372 \[skip ci]
  ([`7352ce8`](homeassistant-ai/ha-mcp@7352ce8))
- **addon**: Publish dev addon version 7.6.0.dev371 \[skip ci]
  ([`d7601f5`](homeassistant-ai/ha-mcp@d7601f5))
- Sync tool docs after merge \[skip ci]
  ([`d0a4482`](homeassistant-ai/ha-mcp@d0a4482))
- **addon**: Publish dev addon version 7.6.0.dev370 \[skip ci]
  ([`f83c32c`](homeassistant-ai/ha-mcp@f83c32c))
- **addon**: Publish dev addon version 7.6.0.dev369 \[skip ci]
  ([`ea16661`](homeassistant-ai/ha-mcp@ea16661))
- **addon**: Publish dev addon version 7.6.0.dev368 \[skip ci]
  ([`af49ea5`](homeassistant-ai/ha-mcp@af49ea5))
- **addon**: Publish dev addon version 7.6.0.dev367 \[skip ci]
  ([`7dd5f4c`](homeassistant-ai/ha-mcp@7dd5f4c))
- **addon**: Publish dev addon version 7.6.0.dev366 \[skip ci]
  ([`6273b7e`](homeassistant-ai/ha-mcp@6273b7e))
- Sync tool docs after merge \[skip ci]
  ([`4e3fd5f`](homeassistant-ai/ha-mcp@4e3fd5f))
- **addon**: Publish dev addon version 7.6.0.dev365 \[skip ci]
  ([`f3bf17b`](homeassistant-ai/ha-mcp@f3bf17b))
- Sync tool docs after merge \[skip ci]
  ([`5307b60`](homeassistant-ai/ha-mcp@5307b60))
- **addon**: Publish dev addon version 7.6.0.dev364 \[skip ci]
  ([`76ec1ae`](homeassistant-ai/ha-mcp@76ec1ae))
- Sync tool docs after merge \[skip ci]
  ([`be864b5`](homeassistant-ai/ha-mcp@be864b5))
- **addon**: Publish dev addon version 7.6.0.dev363 \[skip ci]
  ([`da10c75`](homeassistant-ai/ha-mcp@da10c75))
- **addon**: Publish dev addon version 7.6.0.dev362 \[skip ci]
  ([`69f4527`](homeassistant-ai/ha-mcp@69f4527))
- **addon**: Publish version 7.6.0 \[skip ci]
  ([`086d75d`](homeassistant-ai/ha-mcp@086d75d))

##### Continuous Integration

- Vendor Puppet add-on as a pinned submodule + retry transient add-on builds
  ([#&#8203;1565](homeassistant-ai/ha-mcp#1565))
- Make sync-tool-docs push resilient to concurrent master advances
  ([#&#8203;1564](homeassistant-ai/ha-mcp#1564))
- Fix stale token-cap comment and harden triage budget tests ([#&#8203;1514](homeassistant-ai/ha-mcp#1514))
  ([#&#8203;1560](homeassistant-ai/ha-mcp#1560))
- **deps**: Bump the github-actions group with 2 updates
  ([#&#8203;1556](homeassistant-ai/ha-mcp#1556))
- Budget triage prompt dynamically under the GitHub Models cap ([#&#8203;1514](homeassistant-ai/ha-mcp#1514))
  ([#&#8203;1522](homeassistant-ai/ha-mcp#1522))
- Reduce Docker Hub pulls in performance-tests workflow
  ([#&#8203;1549](homeassistant-ai/ha-mcp#1549))
- Add JavaScript to the CodeQL code-quality gate
  ([#&#8203;1548](homeassistant-ai/ha-mcp#1548))
- Add CodeQL code-quality CI gate and clear all code-quality findings
  ([#&#8203;1526](homeassistant-ai/ha-mcp#1526))
- Drop tool list from evaluate prompt + tighten caps to fit 8K token limit
  ([`6f47c92`](homeassistant-ai/ha-mcp@6f47c92))
- Remove broken maintainer check (GITHUB\_TOKEN lacks read:org)
  ([`010ec58`](homeassistant-ai/ha-mcp@010ec58))
- Switch evaluate step to gpt-4o-mini (16K token free tier vs 8K on gpt-4.1)
  ([#&#8203;1496](homeassistant-ai/ha-mcp#1496))
- Issue bot v2 — GitHub Models triage, needs-info auto-close, duplicate detection
  ([#&#8203;1442](homeassistant-ai/ha-mcp#1442))

##### Refactoring

- Extract settings-UI JavaScript and CSS to separate files
  ([#&#8203;1505](homeassistant-ai/ha-mcp#1505))
- Collapse settings-UI route registration into one table
  ([#&#8203;1504](homeassistant-ai/ha-mcp#1504))

##### Testing

- **oauth**: Add HTTP smoke tests for OAuth metadata-discovery endpoints
  ([#&#8203;1562](homeassistant-ai/ha-mcp#1562))
- **uat**: Relabel c01 as a mode-discrimination probe (taxonomy consistency)
  ([#&#8203;1563](homeassistant-ai/ha-mcp#1563))
- **uat**: Rework c01 routing probe to ha\_search registry-listing mode after [#&#8203;1529](homeassistant-ai/ha-mcp#1529)
  ([#&#8203;1559](homeassistant-ai/ha-mcp#1559))
- **uat**: Add response\_contains\_any check and always log agent responses
  ([#&#8203;1537](homeassistant-ai/ha-mcp#1537))
- **uat**: Treat request timeout as per-story failure, not suite abort
  ([#&#8203;1536](homeassistant-ai/ha-mcp#1536))
- **uat**: Harden BAT story runner against agent crashes
  ([#&#8203;1535](homeassistant-ai/ha-mcp#1535))
- **uat**: Log model and quantization in BAT story results
  ([#&#8203;1525](homeassistant-ai/ha-mcp#1525))
- Capture reasoning tokens and detect inert --no-think in BAT openai agent
  ([#&#8203;1524](homeassistant-ai/ha-mcp#1524))

</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:eyJjcmVhdGVkSW5WZXIiOiI0My4yMTQuNiIsInVwZGF0ZWRJblZlciI6IjQzLjIxNC42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/243
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.

2 participants