feat: web-based settings UI for per-tool enable/disable/pin - #960
Conversation
Add a self-contained HTML settings page served via FastMCP's custom_route at /settings. Provides searchable, grouped tool management with three states per tool: enabled, pinned, disabled. - GET /settings — serves the settings page (inline HTML/CSS/JS) - GET /api/settings/tools — returns tool metadata + current states - POST /api/settings/tools — saves states, applies immediately via mcp.disable()/mcp.enable() (no restart needed for tool changes) - Persists to tool_config.json in addon data dir or ~/.ha-mcp/ - Seeds from DISABLED_TOOLS/PINNED_TOOLS env vars on first run - Mandatory tools (ha_search_entities, ha_get_overview, ha_get_state, ha_report_issue) shown grayed out, cannot be disabled - enable_yaml_config_editing toggle respected as override - Works across all install methods (addon, Docker, standalone) - Dark theme matching HA aesthetic Closes homeassistant-ai#798 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary of ChangesHello, 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 enhances the Home Assistant MCP server by adding a self-contained web-based settings interface. This allows users to dynamically manage tool visibility and pinning directly through their browser, improving usability across various deployment environments like Docker and Home Assistant Add-ons. The changes ensure that tool configurations are persisted and applied immediately, while maintaining safety constraints for critical system tools. Highlights
Using Gemini Code AssistThe 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
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a web-based settings UI at /settings to manage MCP tool visibility (enabling, disabling, and pinning tools) without requiring a server restart. It includes new configuration fields in Settings, logic for persisting these choices to a tool_config.json file, and unit tests for the persistence and visibility logic. Several improvements are needed regarding adherence to repository standards: error responses must use the structured format from errors.py, imports should be consolidated to avoid duplication of constants like DEFAULT_PINNED_TOOLS, and exception handling should be narrowed from broad Exception blocks to specific types like OSError or json.JSONDecodeError. Additionally, a brittle file path used for metadata lookup should be addressed to ensure reliability across different installation environments.
- Import DEFAULT_PINNED_TOOLS from transforms (avoid duplication) - Catch specific exceptions (OSError, json.JSONDecodeError) instead of broad Exception - Fix mypy no-any-return: annotate json.loads return types - Fix ruff C401: use set comprehension instead of set(generator) - Fix ruff C420: use dict.fromkeys instead of dict comprehension - Use structured error format in POST endpoint responses - Make tools.json path discovery check multiple locations - Fix ValueError/TypeError catch for JSON parsing in POST handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kingpanther13
left a comment
There was a problem hiding this comment.
✅ All Gemini comments addressed in 0089c27:
- Structured error responses: POST endpoint now returns
{"success": false, "error": {"code": "...", "message": "..."}}format - Import DEFAULT_PINNED_TOOLS: Now imported from
ha_mcp.transformsinstead of duplicating - Specific exceptions: Changed
except Exceptiontoexcept (OSError, json.JSONDecodeError)andexcept OSErrorthroughout - Brittle tools.json path: Added
_find_tools_json()that checks multiple candidate paths, with runtime fallback to FastMCP tool manager if file not found
…only, dev79 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Enable ingress (shows "Open Web UI" button on addon info page) - Add disabled_tools/pinned_tools text fields as seed/fallback - Add translations for the new fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add ingress_stream: true for WebSocket passthrough - Remove panel_icon (not needed for "Open Web UI" button) - Serve settings page at both / and /settings so ingress root works - Ingress proxies to http://localhost:9583/ which needs a handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…only, dev80 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…only, dev81 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace broken _tool_manager._tools internal API with the public
await mcp.list_tools() call
- Remove tools.json fallback — was a dev-only path, wouldn't exist in
production containers anyway
- Use relative './api/settings/tools' fetch URLs so requests work both
directly and through ingress proxy (ESPHome/Node-RED pattern)
- Fix translation description wording ("on the addon info page")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…only, dev82 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uping fix Settings UI rework: - Replace dropdown with two toggles (enabled + pinned) per tool - Pinned toggle disabled/grayed out when enabled toggle is off - Add banner note explaining pinning only applies with tool search - Show feature-gated tools (ha_config_set_yaml, filesystem tools) as stub entries with a "Requires X in add-on config" note — their toggles are locked since they can't be enabled at runtime Tool grouping fix: - Use local_provider._list_tools() to see ALL registered tools regardless of runtime enable state (so users can re-enable them) - Sort tags alphabetically and prefer non-secondary tags for primary group (Device Registry instead of Z-Wave for ha_get_device) Config additions: - tool_search_max_results field in addon-dev config.yaml + translations - disabled_tools/pinned_tools text fields as seed values - start.py wires all new env vars through Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…only, dev83 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Persist open groups in a Set that survives re-renders (fixes collapse-on-toggle-click bug where clicking any tool toggle would call render() and wipe the expanded state) - Add master enable/disable toggle per group in the header - Master toggle affects all non-mandatory, non-feature-gated tools - Stop propagation on master toggle so clicking it doesn't also expand/collapse the group Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runtime mcp.enable/disable calls accumulate visibility transforms in the provider's _transforms list every save, causing stale transforms to pile up. More importantly, they don't reliably remove tools from the LLM's tool list in practice — tools still appear in list_tools() output with full schema, just fail at call time with "Unknown tool". New approach: save changes to tool_config.json and require an add-on restart. Startup-time apply_tool_visibility() reads the config and applies visibility once, cleanly. Disabled tools are then fully absent from list_tools() on next startup. - Remove runtime mcp.enable/disable from POST handler - Show "Saved — restart required" status after save - Show prominent red restart-required banner in UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New POST /api/settings/restart endpoint calls http://supervisor/addons/self/restart with SUPERVISOR_TOKEN - New GET /api/settings/info exposes whether running as add-on - Frontend shows "Restart Add-on" button in the restart notice banner (only visible when running as add-on) - Click opens a confirmation dialog and triggers the restart Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…only, dev84 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Supervisor kills our process while the restart request is in flight, causing httpx to throw ReadError/RemoteProtocolError. That's actually the SUCCESS path — the restart is happening. Catch those specifically and return success. Also surface the real Supervisor error message in the browser when a real failure occurs (was showing generic "Restart failed" before). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
387aab8 to
1b05ca9
Compare
Add-on DOCS.md: - Document new options: enable_skills, enable_skills_as_tools, enable_tool_search, enable_yaml_config_editing, tool_search_max_results, disabled_tools, pinned_tools - Add "Tool Settings Web UI" section explaining the web UI features, restart requirement, and text-field fallback .env.example: - Add DISABLED_TOOLS, PINNED_TOOLS, TOOL_SEARCH_MAX_RESULTS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a web-based settings UI at /settings to manage MCP tool visibility (enabling, disabling, and pinning tools) across different installation methods. It includes updates to the Home Assistant add-on configuration, documentation, and startup scripts to support these new visibility settings and a tool search result limit. Feedback focuses on adhering to the project's structured error response format, improving type hinting for settings, avoiding brittle private method access in FastMCP, and validating user-provided configuration before persistence.
- Use create_error_response/ErrorCode for all REST endpoint errors (settings save, restart, validation) - Validate states dict from client (string keys, allowed state values) before persisting to disk - Type hint settings parameter as Settings (TYPE_CHECKING import) - Initialize _user_pinned_tools in __init__ instead of getattr fallback - Add clearer docstring explaining why _list_tools() (private API) is used: public list_tools() filters disabled tools, and the settings UI specifically needs the unfiltered list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kingpanther13
left a comment
There was a problem hiding this comment.
✅ All Gemini review feedback addressed in 9c6e8de:
- Structured errors: REST endpoints (
/api/settings/tools,/api/settings/restart) now usecreate_error_response/ErrorCodefromerrors.py - States validation: client-supplied tool states are validated (string keys, allowed state values) before persisting
- Settings type hint:
settingsparameter typed asSettingsviaTYPE_CHECKINGimport _user_pinned_tools: now initialized in__init__, accessed directly instead ofgetattr_list_tools()private API: added clearer docstring explaining why the unfiltered list is needed (publiclist_tools()filters disabled tools, settings UI needs to show them so users can re-enable)
|
Hey @kingpanther13 — ran my Claude over this PR with my current review filters as a sanity-check pass; these are my 2 cents on top of what's already in the thread. Solid feature overall, the apply-on-restart model is the right call and the Gemini round was thoroughly addressed. I have two items I'd want resolved before merge plus a handful of smaller ones; happy to be talked out of any of them. VerdictTwo HIGH items (one auth-surface question, one orphaned setting), one MEDIUM bug (POST handler crashes on non-dict body), plus a small batch of MEDIUM/LOW items and a couple of Boy-Scout opportunities. Nothing architectural. BlockersG1 —
|
Conflicts:
- homeassistant-addon-dev/{config.yaml,translations/en.yaml,DOCS.md,start.py}:
combined homeassistant-ai#1030 beta flags (filesystem/yaml/custom-component) with this
PR's new dev-only options (tool_search_max_results, disabled_tools,
pinned_tools). New options stay dev-only per homeassistant-ai#942 channel convention.
- src/ha_mcp/server.py: combined homeassistant-ai#955's _apply_search_keyword_enrichment
refactor with this PR's settings-visibility apply step. Order:
tools -> enhanced -> skills -> _apply_settings_visibility ->
_apply_search_keyword_enrichment -> _apply_tool_search.
- homeassistant-addon/start.py: kept homeassistant-ai#806 migrate_skills_as_tools_default
+ relocated supervisor-token validation; added new env var exports.
Patch76 review fixes:
- G1: Mount settings UI under MCP secret_path so Docker/standalone clients
share the same auth-by-obscurity as the MCP endpoint. Add-on continues
to mount at root for HA ingress proxy. Routes don't register at all
when neither path is available (stdio mode, or HTTP without secret).
Moved register_settings_routes out of _initialize_server into the HTTP
entry points (_run_http_server, _run_oauth_server, addon start.py).
- G2: Wire tool_search_max_results through CategorizedSearchTransform;
enforce 2-10 range in Pydantic Field and addon-dev schema int(2,10)?.
- G3: 400 instead of 500 when POST body is JSON but not an object.
- G4: Use SUPERVISOR_TOKEN, not /data existence, to detect add-on mode
in _get_config_path. Matches the rest of the module.
- G5: HTML-escape interpolated tool metadata in the settings JS.
- G6: Comment explaining MANDATORY_TOOLS vs DEFAULT_PINNED_TOOLS overlap.
- G7: Add ha_install_mcp_tools stub to FEATURE_GATED_TOOLS; rewrite stub
copy to point at docs/beta.md (covers both stable and dev paths post-homeassistant-ai#942).
- G9: Keep enable_yaml_config_editing guard with defense-in-depth comment;
drop the discard so AND semantics apply (UI off OR toggle off -> tool off).
- G12: Restore .env.example trailing newline.
Tests cover non-dict body, garbage state values, route mounting under
secret_path, _get_config_path env-driven path, FEATURE_GATED_TOOLS
beta-system alignment, and the G9 AND-semantics regression.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mypy rejects the _DeferredMCP wrapper as FastMCP[Any]. _get_server() forces the lazy init, then we pass server.mcp (the real FastMCP) into register_settings_routes. register_browser_landing is left alone since its signature already accepts the union FastMCP | _DeferredMCP. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@Patch76 — addressed in 72cd3ca (merge + bulk fixes) and 3477a39 (mypy followup). Quick rundown: What was implemented
Tests added in
|
Patch76
left a comment
There was a problem hiding this comment.
Re-review on top of 72cd3ca + 3477a39 — all 14 findings addressed cleanly.
Spot-checked load-bearing fixes:
- G1 (auth):
register_settings_routesrefuses to mount when neitherSUPERVISOR_TOKENnorsecret_pathis available; secret-path mount in non-add-on mode matches the MCP-endpoint auth-by-obscurity model. Relocating registration out of_initialize_serverinto the HTTP entry points closes G10 as a side effect — clean. - G2:
max_resultswired atserver.py:543,Field(5, ge=2, le=10)plusint(2,10)?in the dev addon schema. Stable-channel deferral to the release pipeline is consistent with the post-#942 convention. - G3 / G5 / G9.2:
isinstance(body, dict)guard,escapeHtmlon every interpolation, and AND-semantics regression test all in place.
Declined items (G2.3 / G8 / G13 / G14): reasoning holds, nothing I'd push back on.
One non-blocking note: the restart endpoint's missing-SUPERVISOR_TOKEN branch (settings_ui.py:780) and the drop-connection-as-success branch (settings_ui.py:798) still don't have tests — the rest of my coverage list is now covered. Two short cases would close the loop; happy to follow up post-merge instead.
Approving.
🧪 Your changes are now in the dev channel!Your PR has been merged to master and is available for testing in the dev channel. Test your changes before the next stable release (biweekly Wednesday): Quick start# Run dev version
uvx ha-mcp-dev
# Check version
uvx ha-mcp-dev --versionDocker: docker pull ghcr.io/homeassistant-ai/ha-mcp:dev
docker run --rm -i \
-e HOMEASSISTANT_URL=http://your-ha:8123 \
-e HOMEASSISTANT_TOKEN=your_token \
ghcr.io/homeassistant-ai/ha-mcp:devFound an issue? Please open a new bug report and mention this PR for context. |
|
Hey I actually had auto-merge disabled on purpose for this one, I wanted to verify how it looked in my browser/the HA UI again before merging it, were you able to look at that yourself? If not no worries I will check on the dev server when I get a chance |
|
That should have been posted in our private maintainer chat, but thanks for the heads up. Surprised it made it this far, I'll find the token it's using and kill it. |
Adds unit-test coverage for the two previously-untested branches in settings_ui.py:_restart_addon: - Missing SUPERVISOR_TOKEN (settings_ui.py:780-789) — non-addon installs hit this when the user clicks Restart against a Docker/pyinstaller setup; the structured 400 must surface rather than ever reaching the Supervisor URL. - Connection-drop-as-success (settings_ui.py:798-801) — the Supervisor kills our process mid-request during a restart, so a ReadError / RemoteProtocolError / ConnectError from the POST is the documented success signal. Mirrors the _capture_handler pattern from TestSaveToolsValidation. The fixture-level server.settings.verify_ssl = True is required by this PR's post-G1 access path (httpx accepts only bool/SSLContext for verify=). Boy-Scout fix while already touching _restart_addon for the verify_ssl-propagation refactor — closes the test-coverage gap I'd flagged in homeassistant-ai#960's approve-body but never followed up on at the time.
…ha_list_resources / ha_read_resource The two skills toggles were redundant once homeassistant-ai#960 (web settings UI) shipped: the original justifications — "avoid local-skill conflicts" (skills served via skill:// URIs and locally-installed skills under ~/.claude/skills/ live in separate namespaces) and "don't burn extra context" (resources are not auto-injected; list_resources only returns metadata) — don't survive scrutiny. Per-tool visibility is now the single mechanism; users who want ha_list_resources or ha_read_resource off can disable them from the web settings UI like any other tool. Changes: - Drop enable_skills, enable_skills_as_tools, _skills_dependency validator from src/ha_mcp/config.py. - Always register SkillsDirectoryProvider and the ResourcesAsTools transform in server._register_skills(); always include the skills hint in _build_skills_instructions and the search-tool description. - Rename the transform-generated tools list_resources / read_resource to ha_list_resources / ha_read_resource via a HaResourcesAsTools subclass so they follow the project's ha_<verb>_<noun> convention. - Surface the renamed pair in the settings UI: extend _get_tool_metadata with TRANSFORM_GENERATED_TOOLS stub injection, so users can disable them per-tool (FastMCP's local_provider doesn't see transform-appended tools). - Drop add-on toggles: enable_skills / enable_skills_as_tools removed from homeassistant-addon{,-dev}/config.yaml schema + options, the env-var setup and the .skills_as_tools_default_migration_v1 migration helper removed from homeassistant-addon/start.py, and the supervisor labels removed from homeassistant-addon-dev/translations/en.yaml (prod translations sync at the next biweekly release). - Simplify best_practice_checker: drop get_skill_prefix() and its callers; always reference skill:// URIs. - Tests: replace the obsolete enable_skills* assertions with field-removal assertions, drop TestSkillsAsToolsMigration, update unit tests for unconditional registration, add e2e coverage that ha_list_resources / ha_read_resource appear (and unprefixed names don't), add a unit test that _get_tool_metadata injects stubs when local_provider omits them. - README and homeassistant-addon-dev/DOCS.md: remove the toggle rows; document the per-tool visibility path. Migration note: existing users with ENABLE_SKILLS_AS_TOOLS=false lose the env-var path. Equivalent opt-out is available per-tool in the web settings UI, documented in DOCS.md and README. Same outcome is achievable via the alternate mechanism, so this is not a breaking change per AGENTS.md's definition. Closes homeassistant-ai#1133
…ix (#1136) * feat: drop ENABLE_SKILLS / ENABLE_SKILLS_AS_TOOLS toggles, rename to ha_list_resources / ha_read_resource The two skills toggles were redundant once #960 (web settings UI) shipped: the original justifications — "avoid local-skill conflicts" (skills served via skill:// URIs and locally-installed skills under ~/.claude/skills/ live in separate namespaces) and "don't burn extra context" (resources are not auto-injected; list_resources only returns metadata) — don't survive scrutiny. Per-tool visibility is now the single mechanism; users who want ha_list_resources or ha_read_resource off can disable them from the web settings UI like any other tool. Changes: - Drop enable_skills, enable_skills_as_tools, _skills_dependency validator from src/ha_mcp/config.py. - Always register SkillsDirectoryProvider and the ResourcesAsTools transform in server._register_skills(); always include the skills hint in _build_skills_instructions and the search-tool description. - Rename the transform-generated tools list_resources / read_resource to ha_list_resources / ha_read_resource via a HaResourcesAsTools subclass so they follow the project's ha_<verb>_<noun> convention. - Surface the renamed pair in the settings UI: extend _get_tool_metadata with TRANSFORM_GENERATED_TOOLS stub injection, so users can disable them per-tool (FastMCP's local_provider doesn't see transform-appended tools). - Drop add-on toggles: enable_skills / enable_skills_as_tools removed from homeassistant-addon{,-dev}/config.yaml schema + options, the env-var setup and the .skills_as_tools_default_migration_v1 migration helper removed from homeassistant-addon/start.py, and the supervisor labels removed from homeassistant-addon-dev/translations/en.yaml (prod translations sync at the next biweekly release). - Simplify best_practice_checker: drop get_skill_prefix() and its callers; always reference skill:// URIs. - Tests: replace the obsolete enable_skills* assertions with field-removal assertions, drop TestSkillsAsToolsMigration, update unit tests for unconditional registration, add e2e coverage that ha_list_resources / ha_read_resource appear (and unprefixed names don't), add a unit test that _get_tool_metadata injects stubs when local_provider omits them. - README and homeassistant-addon-dev/DOCS.md: remove the toggle rows; document the per-tool visibility path. Migration note: existing users with ENABLE_SKILLS_AS_TOOLS=false lose the env-var path. Equivalent opt-out is available per-tool in the web settings UI, documented in DOCS.md and README. Same outcome is achievable via the alternate mechanism, so this is not a breaking change per AGENTS.md's definition. Closes #1133 * fix: address PR review — harden rename, fix lingering read_resource refs, add coverage Review feedback from the pr-review-toolkit agents (code-reviewer, pr-test-analyzer, silent-failure-hunter, type-design-analyzer, comment-analyzer): - Update LLM-facing strings still mentioning the unprefixed `read_resource` in `_register_skill_guidance_tools` (tool description + handler `how_to_use`) and the `_register_skill_guidance_tools` docstring; also update the `tests/uat/stories/catalog/s13_dashboard_update_existing.yaml` expected-tools list. - Replace the `result[-2:]` slicing in `HaResourcesAsTools.list_tools` with a name-based scan over the full upstream sequence and a `_RENAMES` class mapping. Log a warning if the matched count is not exactly two so a future fastmcp regression that drops or reorders the appended tools surfaces loudly at boot instead of silently leaking the unprefixed names. - Use `HaResourcesAsTools.LIST_TOOL_NAME` / `READ_TOOL_NAME` constants for the pinned-tools list and the search-tool description text in `server.py` so the rename has a single source of truth. - Add an upgrade-fragility note to the `HaResourcesAsTools` docstring flagging the dependency on fastmcp's `_make_*_tool` private factories. - Tighten the `best_practice_checker` module docstring: the `skill_prefix` kwarg note now says "any URL prefix (e.g., a GitHub mirror)" instead of implying a canonical alternative still exists in the module. - Document the cross-module `TRANSFORM_GENERATED_TOOLS` ↔ `HaResourcesAsTools` constant invariant and add `test_transform_generated_tool_names_match_class_constants`. - New unit test file `tests/src/unit/test_ha_resources_as_tools.py`: rename happy-path for `list_tools`/`get_tool`, fall-through for unprefixed and unrelated names, and a drift-warning test that monkey-patches the base class to drop one of the appended tools. - New e2e tests `test_ha_list_resources_invocation` and `test_ha_read_resource_invocation` in `tests/src/e2e/tools/test_skills_resources.py`: actually invoke the renamed tools via `mcp_client.call_tool(...)` to confirm the rename doesn't break dispatch routing (catalog presence is necessary but not sufficient). Note on Gemini's two inline comments: both are based on a non-existent fastmcp API (`Transform.call_tool` / `CallToolNext`) — fastmcp's transform protocol exposes `list_tools` and `get_tool`, and tool dispatch in `FastMCP.call_tool` runs through `get_tool` (which this subclass overrides) before invoking `tool.run`. The new e2e invocation tests verify this end-to-end. * fix: address remaining review items — marker cleanup, skill summary log, ToolStub typing Three follow-ups from the pr-review-toolkit feedback that were initially deferred but on reconsideration belong in this PR: - ``cleanup_stale_migration_marker`` in ``homeassistant-addon/start.py`` removes ``/data/.skills_as_tools_default_migration_v1`` on next boot. The marker was created by the previous version's ``migrate_skills_as_tools_default`` (deleted in this PR's first commit); leaving it on disk forever is permanent ``/data`` litter for every existing add-on install. ``unlink(missing_ok=True)`` plus best-effort error handling. - ``HomeAssistantSmartMCPServer._register_skills`` now tracks per-phase status (provider / transform / guidance_tools count) and emits one summary log line at the end via ``_log_skill_registration_summary`` — ``info`` when both provider and transform succeeded, ``warning`` otherwise. Without the toggle, every install runs this code path on every boot, so a single line operators can grep for is more useful than reconstructing state from scattered ``logger.exception`` calls. Per-phase exception logs remain for stack traces. - ``ToolStub`` ``TypedDict`` defined in ``settings_ui.py`` and applied to both ``TRANSFORM_GENERATED_TOOLS`` and ``FEATURE_GATED_TOOLS``, with ``NotRequired`` keys for ``disabled_by``, ``readOnlyHint``, ``destructiveHint``. Annotation values converted from ``"true"`` strings to actual ``bool``. ``_render_stub`` helper collapses the previously-duplicated stub-build loops into one function so a misspelled key now fails type-checking instead of silently producing an entry with the wrong shape. Behavioral output is unchanged. --------- Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com>
Merge brings in homeassistant-ai#1126, homeassistant-ai#1135, homeassistant-ai#1136, homeassistant-ai#1138 and the dev-addon publish chain since the branch's previous head `147ad5f`. Conflict in `tests/src/unit/test_settings_ui.py` resolved by keeping both adjacent additions: master's `test_returns_500_when_save_fails` (read-only-fs 500-surfacing test from homeassistant-ai#1138) inside `TestSaveToolsValidation`, plus this PR's new `TestRestartAddon` class right after. KP13 round-1 review asks (CHANGES_REQUESTED 2026-05-06 20:38 UTC) all addressed: 1. **Narrow connection-drop catch** — the `except` tuple in `_restart_addon` (in `settings_ui.py`) is now `(httpx.ReadError, httpx.RemoteProtocolError)`. `httpx.ConnectError` is no longer treated as a successful restart; it falls through to the `httpx.HTTPError` handler returning 502 + `CONNECTION_FAILED`. Inline comment documents the deliberate exclusion (DNS / TCP-refused / supervisor-socket-misconfigured all mean Supervisor was unreachable, not that a restart was initiated). 2. **Parametrize connection-drop test** + separate `ConnectError` → 502 case. `test_treats_connection_drop_as_success` now parametrizes over `(httpx.ReadError, httpx.RemoteProtocolError)`. New `test_connect_error_returns_502` locks the contract that a connection-failure-before-handshake surfaces as 502. 3. **Boy-Scout: pin remaining `_restart_addon` branches.** Two new tests: `test_generic_http_error_returns_502` (uses `httpx.PoolTimeout` to exercise the `httpx.HTTPError` fall-through) and `test_supervisor_4xx_returns_502` (Supervisor returns 401 → handler maps to 502). 4. **Symbol-based test docstrings** — class-docstring + method docstrings now reference "the `if not token:` guard", "the catch on `(ReadError, RemoteProtocolError)`", "the `httpx.HTTPError` handler", "the `status_code >= 400` branch" instead of line numbers that shift with every kwarg-split / refactor. 5. **Top-level `import httpx`** in `tests/src/unit/test_settings_ui.py` replaces the inline `__import__("httpx").ReadError(...)` workaround. 6. **Trim "post-G1 state"** from the `verify_ssl = True` fixture comment. Kept the substantive part ("must resolve to a real bool, not a MagicMock, because httpx accepts only bool/SSLContext for `verify=`") that pays off in 6 months. 7. **Move homeassistant-ai#960 cross-reference** out of the `TestRestartAddon` class docstring. Closed-PR review history rots fast in source; the PR body is the right place for it. Local: 1762 unit tests pass, ruff lint + format clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#1128) * refactor: pass verify_ssl to remaining direct-Supervisor httpx callers Closes #1127. Mirrors the verify=self.verify_ssl propagation pattern established in #1126 (rest_client.py:_get_addon_logs_via_supervisor) at the two other direct-Supervisor httpx call sites: - tools_bug_report.py:_fetch_addon_logs uses get_global_settings().verify_ssl (module-level helper, no self/closure context). - settings_ui.py:_restart_addon uses server.client.verify_ssl (closure has access to server: HomeAssistantSmartMCPServer). Both paths effectively propagate Settings.verify_ssl via the access route appropriate to each call site's scope. The http://supervisor URL is plain HTTP and TLS-irrelevant in practice — the parameter keeps all three constructor sites consistent with the established HomeAssistantClient pattern. * refactor: read verify_ssl from server.settings instead of server.client Per Gemini review on PR #1128: server.client is a lazy @Property (server.py) — accessing it for a single config bool would instantiate the full HomeAssistantClient (httpx pool, settings re-read, log line) on first access. server.settings is eager-initialized in the HomeAssistantSmartMCPServer constructor and is the canonical source of truth for verify_ssl. Additional benefit: in OAuth deployment mode (__main__.py:868), HomeAssistantSmartMCPServer is constructed with an OAuthProxyClient whose __getattr__ proxies to a per-request OAuth client requiring an authenticated request context. _restart_addon is a plain admin POST without that context, so server.client.verify_ssl could have surfaced as an auth error in OAuth mode. server.settings.verify_ssl sidesteps it without depending on OAuthProxyClient's attribute-forwarding semantics. * test: pin _restart_addon untested branches per Boy-Scout Adds unit-test coverage for the two previously-untested branches in settings_ui.py:_restart_addon: - Missing SUPERVISOR_TOKEN (settings_ui.py:780-789) — non-addon installs hit this when the user clicks Restart against a Docker/pyinstaller setup; the structured 400 must surface rather than ever reaching the Supervisor URL. - Connection-drop-as-success (settings_ui.py:798-801) — the Supervisor kills our process mid-request during a restart, so a ReadError / RemoteProtocolError / ConnectError from the POST is the documented success signal. Mirrors the _capture_handler pattern from TestSaveToolsValidation. The fixture-level server.settings.verify_ssl = True is required by this PR's post-G1 access path (httpx accepts only bool/SSLContext for verify=). Boy-Scout fix while already touching _restart_addon for the verify_ssl-propagation refactor — closes the test-coverage gap I'd flagged in #960's approve-body but never followed up on at the time. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…→ 7.5.0) (#455) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/homeassistant-ai/ha-mcp](https://github.com/homeassistant-ai/ha-mcp) | minor | `7.4.0` → `7.5.0` | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/3) for more information. --- ### Release Notes <details> <summary>homeassistant-ai/ha-mcp (ghcr.io/homeassistant-ai/ha-mcp)</summary> ### [`v7.5.0`](https://github.com/homeassistant-ai/ha-mcp/blob/HEAD/CHANGELOG.md#v750-2026-05-13) [Compare Source](homeassistant-ai/ha-mcp@v7.4.0...v7.5.0) ##### Added - Add ENABLE\_LITE\_DOCSTRINGS beta toggle ([#​1259](homeassistant-ai/ha-mcp#1259)) - Add ha\_call\_event tool for publishing events on the HA event bus ([#​996](homeassistant-ai/ha-mcp#996)) ([#​1239](homeassistant-ai/ha-mcp#1239)) - Pinpoint backslash-escape mistake in python\_sandbox errors ([#​1204](homeassistant-ai/ha-mcp#1204)) - Reject empty-trigger automations targeting scene.create ([#​1187](homeassistant-ai/ha-mcp#1187)) - Add scene config tools — ha\_config\_get/set/remove\_scene ([#​1168](homeassistant-ai/ha-mcp#1168)) - **addon**: Optional OAuth 2.1 mode for webhook proxy (beta) ([#​1184](homeassistant-ai/ha-mcp#1184)) - Surface helper schema inline in ha\_config\_set\_helper validation errors ([#​1149](homeassistant-ai/ha-mcp#1149)) ([#​1179](homeassistant-ai/ha-mcp#1179)) - Emit progress via FastMCP Context in long-running tools ([#​1124](homeassistant-ai/ha-mcp#1124)) - Broaden python\_transform AST allowlist + improve error UX ([#​1163](homeassistant-ai/ha-mcp#1163)) - Add ha\_manage\_custom\_tool — sandboxed code execution escape hatch ([#​854](homeassistant-ai/ha-mcp#854)) - Always-on skills; rename list/read resource tools with ha\_ prefix ([#​1136](homeassistant-ai/ha-mcp#1136)) - Expose device\_class + options on ha\_set\_entity / ha\_get\_entity (Show As) ([#​1135](homeassistant-ai/ha-mcp#1135)) - **site**: Inline wizard data into setup.astro, migrate setup nuggets, drop content collections ([#​1120](homeassistant-ai/ha-mcp#1120)) - Add "Advanced debug logging" toggle for kill-signal diagnostics ([#​1117](homeassistant-ai/ha-mcp#1117)) - **yaml**: Scoped lovelace.dashboards.\<url\_path> support (issue [#​1034](homeassistant-ai/ha-mcp#1034)) ([#​1103](homeassistant-ai/ha-mcp#1103)) - Add HA\_VERIFY\_SSL toggle to disable TLS verification ([#​1104](homeassistant-ai/ha-mcp#1104)) - Per-top-level-key config\_hash for ha\_manage\_energy\_prefs ([#​1049](homeassistant-ai/ha-mcp#1049)) ([#​1098](homeassistant-ai/ha-mcp#1098)) - **site**: Add gemini-cli setup notes + compose hardening to wizard ([#​1027](homeassistant-ai/ha-mcp#1027)) ([#​1087](homeassistant-ai/ha-mcp#1087)) - Add convenience modes to ha\_manage\_energy\_prefs ([#​1050](homeassistant-ai/ha-mcp#1050)) ([#​1073](homeassistant-ai/ha-mcp#1073)) - Surface integration log levels in ha\_get\_logs/integration/addon ([#​956](homeassistant-ai/ha-mcp#956)) ([#​1003](homeassistant-ai/ha-mcp#1003)) - Expose allowlist\_external\_dirs in ha\_get\_overview full system\_info ([#​1053](homeassistant-ai/ha-mcp#1053)) - **dashboards**: Unify identifier handling in ha\_config\_\*\_dashboard tools ([#​981](homeassistant-ai/ha-mcp#981)) ([#​1075](homeassistant-ai/ha-mcp#1075)) - Include addon container logs in bug reports ([#​934](homeassistant-ai/ha-mcp#934)) - Add WebSocket response-shaping controls to ha\_manage\_addon ([#​1009](homeassistant-ai/ha-mcp#1009)) - Web-based settings UI for per-tool enable/disable/pin ([#​960](homeassistant-ai/ha-mcp#960)) - **site**: Add OpenCode support to setup wizard ([#​1080](homeassistant-ai/ha-mcp#1080)) ##### Changed - Clarify standard-mode HTTP deployment guidance ([#​1185](homeassistant-ai/ha-mcp#1185)) - Add Cloudflared add-on hostname alternative for tunnel service ([#​1183](homeassistant-ai/ha-mcp#1183)) - Align tool naming convention between AGENTS.md and styleguide ([#​943](homeassistant-ai/ha-mcp#943)) ([#​1174](homeassistant-ai/ha-mcp#1174)) - **addon**: Note tool-list ([#​985](homeassistant-ai/ha-mcp#985 divergence; fix [#​1139](https://github.com/homeassistant-ai/ha-mcp/issues/1139)/[#​1162](https://github.com/homeassistant-ai/ha-mcp/issues/1162) test conflict ([#​1172](homeassistant-ai/ha-mcp#1172)) - Add brew install option for mcp-proxy on macOS ([#​1171](homeassistant-ai/ha-mcp#1171)) - Update contributors list \[contributors-updated] ([`aba01a1`](homeassistant-ai/ha-mcp@aba01a1)) - Warn against enable\_tool\_search on Claude Sonnet/Opus ([#​1088](homeassistant-ai/ha-mcp#1088)) ([#​1140](homeassistant-ai/ha-mcp#1140)) - Address [#​1094](homeassistant-ai/ha-mcp#1094) review nits on OpenCode mirror comments ([#​1105](homeassistant-ai/ha-mcp#1105)) ##### Fixed - **integrations**: Surface ConfigEntry.options via OptionsFlow probe ([#​1245](homeassistant-ai/ha-mcp#1245)) - **backup**: Discover local agent at call time instead of hardcoding hassio.local ([#​1244](homeassistant-ai/ha-mcp#1244)) - Triage all 10 ha\_search\_entities behaviors from [#​1170](homeassistant-ai/ha-mcp#1170) ([#​1195](homeassistant-ai/ha-mcp#1195)) - Replace cron with systemd for demo server (prevents process leak) ([#​1110](homeassistant-ai/ha-mcp#1110)) - Improve ha\_manage\_addon discoverability (BM25 keywords + slug examples) ([#​1200](homeassistant-ai/ha-mcp#1200)) - Route Supervisor 401s to structured tool errors + add E2E coverage ([#​1129](homeassistant-ai/ha-mcp#1129)) ([#​1192](homeassistant-ai/ha-mcp#1192)) - Harden \_validate\_category\_id gate to cover dict-promoted category ([#​1190](homeassistant-ai/ha-mcp#1190)) - Broaden template anti-pattern detection + skill discoverability ([#​1011](homeassistant-ai/ha-mcp#1011)) ([#​1181](homeassistant-ai/ha-mcp#1181)) - Return newest automation traces, add offset+order pagination ([#​1177](homeassistant-ai/ha-mcp#1177)) ([#​1178](homeassistant-ai/ha-mcp#1178)) - **security**: Write YAML backups outside www/ (GHSA-g39v-cvjh-8fpf) ([#​1180](homeassistant-ai/ha-mcp#1180)) - **search**: Apply domain\_filter when area\_filter is set ([#​1162](homeassistant-ai/ha-mcp#1162)) ([#​1165](homeassistant-ai/ha-mcp#1165)) - **resources**: Reject HA-config YAML in dashboard resource content ([#​1160](homeassistant-ai/ha-mcp#1160)) - Close 19 bugs in ha\_config\_set\_helper (issue [#​1150](homeassistant-ai/ha-mcp#1150)) ([#​1151](homeassistant-ai/ha-mcp#1151)) - Route addon log fetches directly to supervisor on addon installs ([#​1126](homeassistant-ai/ha-mcp#1126)) - Survive read-only filesystems at startup ([#​1138](homeassistant-ai/ha-mcp#1138)) - **helpers**: Clarify name-required-on-create for ha\_config\_set\_helper ([#​1143](homeassistant-ai/ha-mcp#1143)) - Resolve disabled entities via entity\_registry in helper deletion ([#​1119](homeassistant-ai/ha-mcp#1119)) - Allow unary operators in python\_transform sandbox ([#​1118](homeassistant-ai/ha-mcp#1118)) - **site**: Add github-copilot-agents wizard branch + delete unreferenced data/clients.ts ([#​1108](homeassistant-ai/ha-mcp#1108)) - **addons**: Route addon API calls through HA Core ingress proxy ([#​1069](homeassistant-ai/ha-mcp#1069)) - **webhook-proxy**: Surface webhook registration failures instead of silently loading ([#​1101](homeassistant-ai/ha-mcp#1101)) - **site**: Resolve client display-order collisions and anchor OpenCode shape ([#​1094](homeassistant-ai/ha-mcp#1094)) ##### Performance Improvements - Dedupe lovelace/dashboards/list in ha\_config\_set\_dashboard ([#​1085](homeassistant-ai/ha-mcp#1085)) ([#​1191](homeassistant-ai/ha-mcp#1191)) ##### Refactoring - Drop obsolete ha\_mcp\_tools defensive ruamel.yaml imports ([post-#​1268](https://github.com/post-/ha-mcp/issues/1268)) ([#​1269](homeassistant-ai/ha-mcp#1269)) - Extract shared Supervisor httpx client helper ([#​1130](homeassistant-ai/ha-mcp#1130)) ([#​1203](homeassistant-ai/ha-mcp#1203)) - Surface client identity, AI model, config toggles, and prompt context in ha\_report\_issue ([#​1189](homeassistant-ai/ha-mcp#1189)) - Harden Context injection with safe-emit + branch coverage ([#​1173](homeassistant-ai/ha-mcp#1173)) - Consolidate area/floor set+remove tools (revisit of [#​813](homeassistant-ai/ha-mcp#813)) ([#​1139](homeassistant-ai/ha-mcp#1139)) - Pass verify\_ssl to remaining direct-Supervisor httpx callers ([#​1128](homeassistant-ai/ha-mcp#1128)) - Validate only new entries on convenience-mode writes ([#​1086](homeassistant-ai/ha-mcp#1086)) ([#​1100](homeassistant-ai/ha-mcp#1100)) *** <details> <summary>Internal Changes</summary> ##### Fixed - **ci**: Align pr.yml E2E with --dist loadscope ([#​1206](homeassistant-ai/ha-mcp#1206)) ([#​1247](homeassistant-ai/ha-mcp#1247)) - **ci**: Switch Renovate to a GitHub App token to allow workflow-file pushes ([#​1229](homeassistant-ai/ha-mcp#1229)) - **ci**: Break gemini-triage retrigger loop and bump turn budget ([#​1131](homeassistant-ai/ha-mcp#1131)) - **ci**: Harden gemini-triage so failures stop spamming user issues ([#​1122](homeassistant-ai/ha-mcp#1122)) - **ci**: Unbreak hotfix-release semantic-release run ([#​1091](homeassistant-ai/ha-mcp#1091)) ##### Chores - **addon**: Publish dev addon version 7.4.1.dev299 \[skip ci] ([`397aa6d`](homeassistant-ai/ha-mcp@397aa6d)) - **addon**: Publish dev addon version 7.4.1.dev298 \[skip ci] ([`942b7e0`](homeassistant-ai/ha-mcp@942b7e0)) - Sync tool docs after merge \[skip ci] ([`6823c47`](homeassistant-ai/ha-mcp@6823c47)) - **addon**: Publish dev addon version 7.4.1.dev297 \[skip ci] ([`6eac062`](homeassistant-ai/ha-mcp@6eac062)) - **addon**: Publish dev addon version 7.4.1.dev296 \[skip ci] ([`b2afe93`](homeassistant-ai/ha-mcp@b2afe93)) - **addon**: Publish dev addon version 7.4.1.dev295 \[skip ci] ([`4f4c4f3`](homeassistant-ai/ha-mcp@4f4c4f3)) - **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.5.1 ([#​1236](homeassistant-ai/ha-mcp#1236)) - **addon**: Publish dev addon version 7.4.1.dev294 \[skip ci] ([`fd24991`](homeassistant-ai/ha-mcp@fd24991)) - **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.13 ([#​1233](homeassistant-ai/ha-mcp#1233)) - **addon**: Publish dev addon version 7.4.1.dev293 \[skip ci] ([`fcc6496`](homeassistant-ai/ha-mcp@fcc6496)) - **addon**: Publish dev addon version 7.4.1.dev292 \[skip ci] ([`2961650`](homeassistant-ai/ha-mcp@2961650)) - **addon**: Publish dev addon version 7.4.1.dev291 \[skip ci] ([`5703112`](homeassistant-ai/ha-mcp@5703112)) - **addon**: Publish dev addon version 7.4.1.dev290 \[skip ci] ([`19b2f65`](homeassistant-ai/ha-mcp@19b2f65)) - **addon**: Publish dev addon version 7.4.1.dev289 \[skip ci] ([`e5a1365`](homeassistant-ai/ha-mcp@e5a1365)) - Sync tool docs after merge \[skip ci] ([`d2ff93b`](homeassistant-ai/ha-mcp@d2ff93b)) - **addon**: Publish dev addon version 7.4.1.dev288 \[skip ci] ([`0f62400`](homeassistant-ai/ha-mcp@0f62400)) - Sync tool docs after merge \[skip ci] ([`c7e2066`](homeassistant-ai/ha-mcp@c7e2066)) - **addon**: Publish dev addon version 7.4.1.dev287 \[skip ci] ([`c1133d4`](homeassistant-ai/ha-mcp@c1133d4)) - **addon**: Publish dev addon version 7.4.1.dev286 \[skip ci] ([`1ae790e`](homeassistant-ai/ha-mcp@1ae790e)) - **addon**: Publish dev addon version 7.4.1.dev285 \[skip ci] ([`2387d0c`](homeassistant-ai/ha-mcp@2387d0c)) - **addon**: Publish dev addon version 7.4.1.dev284 \[skip ci] ([`dd3a4a5`](homeassistant-ai/ha-mcp@dd3a4a5)) - **addon**: Publish dev addon version 7.4.1.dev283 \[skip ci] ([`78af8eb`](homeassistant-ai/ha-mcp@78af8eb)) - Sync tool docs after merge \[skip ci] ([`093fd74`](homeassistant-ai/ha-mcp@093fd74)) - **addon**: Publish dev addon version 7.4.1.dev282 \[skip ci] ([`2141e15`](homeassistant-ai/ha-mcp@2141e15)) - Sync tool docs after merge \[skip ci] ([`7810c95`](homeassistant-ai/ha-mcp@7810c95)) - **addon**: Publish dev addon version 7.4.1.dev281 \[skip ci] ([`7d79ec2`](homeassistant-ai/ha-mcp@7d79ec2)) - Sync tool docs after merge \[skip ci] ([`a73dc81`](homeassistant-ai/ha-mcp@a73dc81)) - **addon**: Publish dev addon version 7.4.1.dev280 \[skip ci] ([`c858ce3`](homeassistant-ai/ha-mcp@c858ce3)) - Sync tool docs after merge \[skip ci] ([`a587be0`](homeassistant-ai/ha-mcp@a587be0)) - **addon**: Publish dev addon version 7.4.1.dev279 \[skip ci] ([`b78ddb2`](homeassistant-ai/ha-mcp@b78ddb2)) - Sync tool docs after merge \[skip ci] ([`1210725`](homeassistant-ai/ha-mcp@1210725)) - **addon**: Publish dev addon version 7.4.1.dev278 \[skip ci] ([`a282c17`](homeassistant-ai/ha-mcp@a282c17)) - **addon**: Publish dev addon version 7.4.1.dev277 \[skip ci] ([`1081768`](homeassistant-ai/ha-mcp@1081768)) - Sync tool docs after merge \[skip ci] ([`e03f5d2`](homeassistant-ai/ha-mcp@e03f5d2)) - **addon**: Publish dev addon version 7.4.1.dev276 \[skip ci] ([`c4ef680`](homeassistant-ai/ha-mcp@c4ef680)) - **addon**: Publish dev addon version 7.4.1.dev275 \[skip ci] ([`780422d`](homeassistant-ai/ha-mcp@780422d)) - Sync tool docs after merge \[skip ci] ([`8a2bd1a`](homeassistant-ai/ha-mcp@8a2bd1a)) - **addon**: Publish dev addon version 7.4.1.dev274 \[skip ci] ([`f0f09de`](homeassistant-ai/ha-mcp@f0f09de)) - **addon**: Publish dev addon version 7.4.1.dev273 \[skip ci] ([`cb49f68`](homeassistant-ai/ha-mcp@cb49f68)) - **addon**: Publish dev addon version 7.4.1.dev272 \[skip ci] ([`5097186`](homeassistant-ai/ha-mcp@5097186)) - **addon**: Publish dev addon version 7.4.1.dev271 \[skip ci] ([`4714342`](homeassistant-ai/ha-mcp@4714342)) - **addon**: Publish dev addon version 7.4.1.dev270 \[skip ci] ([`217982a`](homeassistant-ai/ha-mcp@217982a)) - **addon**: Publish dev addon version 7.4.1.dev269 \[skip ci] ([`a65dd5f`](homeassistant-ai/ha-mcp@a65dd5f)) - Sync tool docs after merge \[skip ci] ([`0e6b54f`](homeassistant-ai/ha-mcp@0e6b54f)) - **addon**: Publish dev addon version 7.4.1.dev268 \[skip ci] ([`60ba1f2`](homeassistant-ai/ha-mcp@60ba1f2)) - **addon**: Publish dev addon version 7.4.1.dev267 \[skip ci] ([`13412aa`](homeassistant-ai/ha-mcp@13412aa)) - Sync tool docs after merge \[skip ci] ([`2702a0f`](homeassistant-ai/ha-mcp@2702a0f)) - **addon**: Publish dev addon version 7.4.1.dev266 \[skip ci] ([`77abe0b`](homeassistant-ai/ha-mcp@77abe0b)) - **addon**: Publish dev addon version 7.4.1.dev265 \[skip ci] ([`08b69db`](homeassistant-ai/ha-mcp@08b69db)) - Sync tool docs after merge \[skip ci] ([`c1f24b5`](homeassistant-ai/ha-mcp@c1f24b5)) - **addon**: Publish dev addon version 7.4.1.dev264 \[skip ci] ([`f2583f6`](homeassistant-ai/ha-mcp@f2583f6)) - Sync tool docs after merge \[skip ci] ([`c2ed2d3`](homeassistant-ai/ha-mcp@c2ed2d3)) - **addon**: Publish dev addon version 7.4.1.dev263 \[skip ci] ([`9d43e54`](homeassistant-ai/ha-mcp@9d43e54)) - **addon**: Publish dev addon version 7.4.1.dev262 \[skip ci] ([`a7355c8`](homeassistant-ai/ha-mcp@a7355c8)) - Sync tool docs after merge \[skip ci] ([`085bd8a`](homeassistant-ai/ha-mcp@085bd8a)) - Convert agents to skills ([#​1084](homeassistant-ai/ha-mcp#1084)) - **addon**: Publish dev addon version 7.4.1.dev261 \[skip ci] ([`0d1af36`](homeassistant-ai/ha-mcp@0d1af36)) - **addon**: Publish dev addon version 7.4.1.dev260 \[skip ci] ([`29397dc`](homeassistant-ai/ha-mcp@29397dc)) - **addon**: Publish dev addon version 7.4.1.dev259 \[skip ci] ([`4bbc74b`](homeassistant-ai/ha-mcp@4bbc74b)) - Sync tool docs after merge \[skip ci] ([`0f6d41e`](homeassistant-ai/ha-mcp@0f6d41e)) - **addon**: Publish dev addon version 7.4.1.dev258 \[skip ci] ([`6751d08`](homeassistant-ai/ha-mcp@6751d08)) - **addon**: Publish dev addon version 7.4.1.dev257 \[skip ci] ([`2213c89`](homeassistant-ai/ha-mcp@2213c89)) - **addon**: Publish dev addon version 7.4.1.dev256 \[skip ci] ([`18a366e`](homeassistant-ai/ha-mcp@18a366e)) - **addon**: Publish dev addon version 7.4.1.dev255 \[skip ci] ([`0e9b18d`](homeassistant-ai/ha-mcp@0e9b18d)) - **addon**: Publish dev addon version 7.4.1.dev254 \[skip ci] ([`39fc65b`](homeassistant-ai/ha-mcp@39fc65b)) - Sync tool docs after merge \[skip ci] ([`9fa0aea`](homeassistant-ai/ha-mcp@9fa0aea)) - **addon**: Publish dev addon version 7.4.1.dev253 \[skip ci] ([`0dcc59e`](homeassistant-ai/ha-mcp@0dcc59e)) - Sync tool docs after merge \[skip ci] ([`ec7413f`](homeassistant-ai/ha-mcp@ec7413f)) - **addon**: Publish dev addon version 7.4.1.dev252 \[skip ci] ([`345640c`](homeassistant-ai/ha-mcp@345640c)) - **addon**: Publish dev addon version 7.4.1.dev251 \[skip ci] ([`bab9d49`](homeassistant-ai/ha-mcp@bab9d49)) - Sync tool docs after merge \[skip ci] ([`726f0a5`](homeassistant-ai/ha-mcp@726f0a5)) - **addon**: Publish dev addon version 7.4.1.dev250 \[skip ci] ([`ded04ea`](homeassistant-ai/ha-mcp@ded04ea)) - **addon**: Publish dev addon version 7.4.1.dev249 \[skip ci] ([`37d5628`](homeassistant-ai/ha-mcp@37d5628)) - **addon**: Publish dev addon version 7.4.1.dev248 \[skip ci] ([`530786a`](homeassistant-ai/ha-mcp@530786a)) - Sync tool docs after merge \[skip ci] ([`36719c3`](homeassistant-ai/ha-mcp@36719c3)) - **addon**: Publish dev addon version 7.4.1.dev247 \[skip ci] ([`4dc47b5`](homeassistant-ai/ha-mcp@4dc47b5)) - **addon**: Publish dev addon version 7.4.1.dev246 \[skip ci] ([`6ffbd6a`](homeassistant-ai/ha-mcp@6ffbd6a)) - Sync tool docs after merge \[skip ci] ([`add66e3`](homeassistant-ai/ha-mcp@add66e3)) - **addon**: Publish dev addon version 7.4.1.dev245 \[skip ci] ([`d0114af`](homeassistant-ai/ha-mcp@d0114af)) - Sync tool docs after merge \[skip ci] ([`0ca41af`](homeassistant-ai/ha-mcp@0ca41af)) - **addon**: Publish dev addon version 7.4.1.dev244 \[skip ci] ([`d052dd0`](homeassistant-ai/ha-mcp@d052dd0)) - **addon**: Publish dev addon version 7.4.0.dev243 \[skip ci] ([`827bc65`](homeassistant-ai/ha-mcp@827bc65)) - Bump package version to 7.4.1 to match released addon ([`4f65497`](homeassistant-ai/ha-mcp@4f65497)) - **addon**: Publish dev addon version 7.4.0.dev242 \[skip ci] ([`8ba80ae`](homeassistant-ai/ha-mcp@8ba80ae)) - **addon**: Publish hotfix version 7.4.1 ([`bda75e6`](homeassistant-ai/ha-mcp@bda75e6)) - **addon**: Publish dev addon version 7.4.0.dev241 \[skip ci] ([`2126428`](homeassistant-ai/ha-mcp@2126428)) ##### Continuous Integration - **deps**: Bump renovatebot/github-action in the github-actions group ([#​1218](homeassistant-ai/ha-mcp#1218)) - **deps**: Bump renovatebot/github-action in the github-actions group ([#​1111](homeassistant-ai/ha-mcp#1111)) ##### Refactoring - Extract \_fetch\_dashboards\_list helper ([#​1193](homeassistant-ai/ha-mcp#1193)) ([#​1207](homeassistant-ai/ha-mcp#1207)) ##### Testing - **e2e**: Module-scope bulk\_automations + bulk\_scripts fixtures (refs [#​366](homeassistant-ai/ha-mcp#366)) ([#​1275](homeassistant-ai/ha-mcp#1275)) - **e2e**: Lower INPUT\_BOOLEAN\_WAIT from 30s to 10s (refs [#​366](homeassistant-ai/ha-mcp#366)) ([#​1273](homeassistant-ai/ha-mcp#1273)) - **e2e**: Generalize readiness-gate diagnostics helper (closes [#​1267](homeassistant-ai/ha-mcp#1267)) ([#​1271](homeassistant-ai/ha-mcp#1271)) - **e2e**: Narrow except clauses in e2e polling helpers (closes [#​1266](homeassistant-ai/ha-mcp#1266)) ([#​1270](homeassistant-ai/ha-mcp#1270)) - **e2e**: Drop ha\_mcp\_tools retry-path + pre-install manifest requirements ([#​1268](homeassistant-ai/ha-mcp#1268)) - **e2e**: Instrument and retry ha\_mcp\_tools readiness wait ([#​1262](homeassistant-ai/ha-mcp#1262)) - Use time.monotonic() in UAT runner and test\_env\_manager ([#​1254](homeassistant-ai/ha-mcp#1254)) - **e2e**: Detect partial/corrupt hacs\_frontend dir in fast-path guard ([#​1253](homeassistant-ai/ha-mcp#1253)) - **e2e**: Remove unused wait/assert helpers ([post-#​1249](https://github.com/post-/ha-mcp/issues/1249) audit) ([#​1256](homeassistant-ai/ha-mcp#1256)) - **e2e**: Clear stale .hacs\_frontend.lock from prior crashed runs ([#​1252](homeassistant-ai/ha-mcp#1252)) - **e2e**: Use time.monotonic() in workflow polling loops ([#​1258](homeassistant-ai/ha-mcp#1258)) - **e2e**: Use time.monotonic() for duration polling ([#​1234](homeassistant-ai/ha-mcp#1234)) ([#​1249](homeassistant-ai/ha-mcp#1249)) - **e2e**: Close ARM ha\_mcp\_tools readiness race under loadscope ([#​1208](homeassistant-ai/ha-mcp#1208)) - **hacs**: Tighten is\_hacs\_unavailable to not match legitimate "Repository not found" ([#​1246](homeassistant-ai/ha-mcp#1246)) - **seed**: Unblock 3 silent-skip pagination/state tests via baked recorder DB ([#​1240](homeassistant-ai/ha-mcp#1240)) - **seed**: Register a writable local\_calendar to unblock event-creation test ([#​1243](homeassistant-ai/ha-mcp#1243)) - **addon**: Fix base64 padding-bit flake in token tamper tests ([#​1238](homeassistant-ai/ha-mcp#1238)) ([#​1241](homeassistant-ai/ha-mcp#1241)) - **seed**: Add a writable scene for test\_call\_service\_scene\_turn\_on ([#​1231](homeassistant-ai/ha-mcp#1231)) - **seed**: Assign demo device to living\_room area for filter test ([#​1230](homeassistant-ai/ha-mcp#1230)) - **e2e**: Drop nonexistent sun service from session readiness wait ([#​1227](homeassistant-ai/ha-mcp#1227)) - **e2e**: Self-contain dashboard register/remove to fix ARM xdist race ([#​1196](homeassistant-ai/ha-mcp#1196)) ([#​1201](homeassistant-ai/ha-mcp#1201)) - Fix EN dash in docstring causing RUF002 lint failure ([`eac5916`](homeassistant-ai/ha-mcp@eac5916)) - Address Gemini review feedback on host detection and port allocation ([`960305e`](homeassistant-ai/ha-mcp@960305e)) - Fix three categories of E2E test flakiness ([`39417ff`](homeassistant-ai/ha-mcp@39417ff)) - **e2e**: Pin config\_hash stability for dashboards ([#​1132](homeassistant-ai/ha-mcp#1132)) </details> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19--> Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/455
….0 ) (#26) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/homeassistant-ai/ha-mcp](https://github.com/homeassistant-ai/ha-mcp) | minor | `7.4.0` → `7.5.0` | --- ### Release Notes <details> <summary>homeassistant-ai/ha-mcp (ghcr.io/homeassistant-ai/ha-mcp)</summary> ### [`v7.5.0`](https://github.com/homeassistant-ai/ha-mcp/blob/HEAD/CHANGELOG.md#v750-2026-05-13) [Compare Source](homeassistant-ai/ha-mcp@v7.4.0...v7.5.0) ##### Added - Add ENABLE\_LITE\_DOCSTRINGS beta toggle ([#​1259](homeassistant-ai/ha-mcp#1259)) - Add ha\_call\_event tool for publishing events on the HA event bus ([#​996](homeassistant-ai/ha-mcp#996)) ([#​1239](homeassistant-ai/ha-mcp#1239)) - Pinpoint backslash-escape mistake in python\_sandbox errors ([#​1204](homeassistant-ai/ha-mcp#1204)) - Reject empty-trigger automations targeting scene.create ([#​1187](homeassistant-ai/ha-mcp#1187)) - Add scene config tools — ha\_config\_get/set/remove\_scene ([#​1168](homeassistant-ai/ha-mcp#1168)) - **addon**: Optional OAuth 2.1 mode for webhook proxy (beta) ([#​1184](homeassistant-ai/ha-mcp#1184)) - Surface helper schema inline in ha\_config\_set\_helper validation errors ([#​1149](homeassistant-ai/ha-mcp#1149)) ([#​1179](homeassistant-ai/ha-mcp#1179)) - Emit progress via FastMCP Context in long-running tools ([#​1124](homeassistant-ai/ha-mcp#1124)) - Broaden python\_transform AST allowlist + improve error UX ([#​1163](homeassistant-ai/ha-mcp#1163)) - Add ha\_manage\_custom\_tool — sandboxed code execution escape hatch ([#​854](homeassistant-ai/ha-mcp#854)) - Always-on skills; rename list/read resource tools with ha\_ prefix ([#​1136](homeassistant-ai/ha-mcp#1136)) - Expose device\_class + options on ha\_set\_entity / ha\_get\_entity (Show As) ([#​1135](homeassistant-ai/ha-mcp#1135)) - **site**: Inline wizard data into setup.astro, migrate setup nuggets, drop content collections ([#​1120](homeassistant-ai/ha-mcp#1120)) - Add "Advanced debug logging" toggle for kill-signal diagnostics ([#​1117](homeassistant-ai/ha-mcp#1117)) - **yaml**: Scoped lovelace.dashboards.\<url\_path> support (issue [#​1034](homeassistant-ai/ha-mcp#1034)) ([#​1103](homeassistant-ai/ha-mcp#1103)) - Add HA\_VERIFY\_SSL toggle to disable TLS verification ([#​1104](homeassistant-ai/ha-mcp#1104)) - Per-top-level-key config\_hash for ha\_manage\_energy\_prefs ([#​1049](homeassistant-ai/ha-mcp#1049)) ([#​1098](homeassistant-ai/ha-mcp#1098)) - **site**: Add gemini-cli setup notes + compose hardening to wizard ([#​1027](homeassistant-ai/ha-mcp#1027)) ([#​1087](homeassistant-ai/ha-mcp#1087)) - Add convenience modes to ha\_manage\_energy\_prefs ([#​1050](homeassistant-ai/ha-mcp#1050)) ([#​1073](homeassistant-ai/ha-mcp#1073)) - Surface integration log levels in ha\_get\_logs/integration/addon ([#​956](homeassistant-ai/ha-mcp#956)) ([#​1003](homeassistant-ai/ha-mcp#1003)) - Expose allowlist\_external\_dirs in ha\_get\_overview full system\_info ([#​1053](homeassistant-ai/ha-mcp#1053)) - **dashboards**: Unify identifier handling in ha\_config\_\*\_dashboard tools ([#​981](homeassistant-ai/ha-mcp#981)) ([#​1075](homeassistant-ai/ha-mcp#1075)) - Include addon container logs in bug reports ([#​934](homeassistant-ai/ha-mcp#934)) - Add WebSocket response-shaping controls to ha\_manage\_addon ([#​1009](homeassistant-ai/ha-mcp#1009)) - Web-based settings UI for per-tool enable/disable/pin ([#​960](homeassistant-ai/ha-mcp#960)) - **site**: Add OpenCode support to setup wizard ([#​1080](homeassistant-ai/ha-mcp#1080)) ##### Changed - Clarify standard-mode HTTP deployment guidance ([#​1185](homeassistant-ai/ha-mcp#1185)) - Add Cloudflared add-on hostname alternative for tunnel service ([#​1183](homeassistant-ai/ha-mcp#1183)) - Align tool naming convention between AGENTS.md and styleguide ([#​943](homeassistant-ai/ha-mcp#943)) ([#​1174](homeassistant-ai/ha-mcp#1174)) - **addon**: Note tool-list ([#​985](homeassistant-ai/ha-mcp#985 divergence; fix [#​1139](https://github.com/homeassistant-ai/ha-mcp/issues/1139)/[#​1162](https://github.com/homeassistant-ai/ha-mcp/issues/1162) test conflict ([#​1172](homeassistant-ai/ha-mcp#1172)) - Add brew install option for mcp-proxy on macOS ([#​1171](homeassistant-ai/ha-mcp#1171)) - Update contributors list \[contributors-updated] ([`aba01a1`](homeassistant-ai/ha-mcp@aba01a1)) - Warn against enable\_tool\_search on Claude Sonnet/Opus ([#​1088](homeassistant-ai/ha-mcp#1088)) ([#​1140](homeassistant-ai/ha-mcp#1140)) - Address [#​1094](homeassistant-ai/ha-mcp#1094) review nits on OpenCode mirror comments ([#​1105](homeassistant-ai/ha-mcp#1105)) ##### Fixed - **integrations**: Surface ConfigEntry.options via OptionsFlow probe ([#​1245](homeassistant-ai/ha-mcp#1245)) - **backup**: Discover local agent at call time instead of hardcoding hassio.local ([#​1244](homeassistant-ai/ha-mcp#1244)) - Triage all 10 ha\_search\_entities behaviors from [#​1170](homeassistant-ai/ha-mcp#1170) ([#​1195](homeassistant-ai/ha-mcp#1195)) - Replace cron with systemd for demo server (prevents process leak) ([#​1110](homeassistant-ai/ha-mcp#1110)) - Improve ha\_manage\_addon discoverability (BM25 keywords + slug examples) ([#​1200](homeassistant-ai/ha-mcp#1200)) - Route Supervisor 401s to structured tool errors + add E2E coverage ([#​1129](homeassistant-ai/ha-mcp#1129)) ([#​1192](homeassistant-ai/ha-mcp#1192)) - Harden \_validate\_category\_id gate to cover dict-promoted category ([#​1190](homeassistant-ai/ha-mcp#1190)) - Broaden template anti-pattern detection + skill discoverability ([#​1011](homeassistant-ai/ha-mcp#1011)) ([#​1181](homeassistant-ai/ha-mcp#1181)) - Return newest automation traces, add offset+order pagination ([#​1177](homeassistant-ai/ha-mcp#1177)) ([#​1178](homeassistant-ai/ha-mcp#1178)) - **security**: Write YAML backups outside www/ (GHSA-g39v-cvjh-8fpf) ([#​1180](homeassistant-ai/ha-mcp#1180)) - **search**: Apply domain\_filter when area\_filter is set ([#​1162](homeassistant-ai/ha-mcp#1162)) ([#​1165](homeassistant-ai/ha-mcp#1165)) - **resources**: Reject HA-config YAML in dashboard resource content ([#​1160](homeassistant-ai/ha-mcp#1160)) - Close 19 bugs in ha\_config\_set\_helper (issue [#​1150](homeassistant-ai/ha-mcp#1150)) ([#​1151](homeassistant-ai/ha-mcp#1151)) - Route addon log fetches directly to supervisor on addon installs ([#​1126](homeassistant-ai/ha-mcp#1126)) - Survive read-only filesystems at startup ([#​1138](homeassistant-ai/ha-mcp#1138)) - **helpers**: Clarify name-required-on-create for ha\_config\_set\_helper ([#​1143](homeassistant-ai/ha-mcp#1143)) - Resolve disabled entities via entity\_registry in helper deletion ([#​1119](homeassistant-ai/ha-mcp#1119)) - Allow unary operators in python\_transform sandbox ([#​1118](homeassistant-ai/ha-mcp#1118)) - **site**: Add github-copilot-agents wizard branch + delete unreferenced data/clients.ts ([#​1108](homeassistant-ai/ha-mcp#1108)) - **addons**: Route addon API calls through HA Core ingress proxy ([#​1069](homeassistant-ai/ha-mcp#1069)) - **webhook-proxy**: Surface webhook registration failures instead of silently loading ([#​1101](homeassistant-ai/ha-mcp#1101)) - **site**: Resolve client display-order collisions and anchor OpenCode shape ([#​1094](homeassistant-ai/ha-mcp#1094)) ##### Performance Improvements - Dedupe lovelace/dashboards/list in ha\_config\_set\_dashboard ([#​1085](homeassistant-ai/ha-mcp#1085)) ([#​1191](homeassistant-ai/ha-mcp#1191)) ##### Refactoring - Drop obsolete ha\_mcp\_tools defensive ruamel.yaml imports ([post-#​1268](https://github.com/post-/ha-mcp/issues/1268)) ([#​1269](homeassistant-ai/ha-mcp#1269)) - Extract shared Supervisor httpx client helper ([#​1130](homeassistant-ai/ha-mcp#1130)) ([#​1203](homeassistant-ai/ha-mcp#1203)) - Surface client identity, AI model, config toggles, and prompt context in ha\_report\_issue ([#​1189](homeassistant-ai/ha-mcp#1189)) - Harden Context injection with safe-emit + branch coverage ([#​1173](homeassistant-ai/ha-mcp#1173)) - Consolidate area/floor set+remove tools (revisit of [#​813](homeassistant-ai/ha-mcp#813)) ([#​1139](homeassistant-ai/ha-mcp#1139)) - Pass verify\_ssl to remaining direct-Supervisor httpx callers ([#​1128](homeassistant-ai/ha-mcp#1128)) - Validate only new entries on convenience-mode writes ([#​1086](homeassistant-ai/ha-mcp#1086)) ([#​1100](homeassistant-ai/ha-mcp#1100)) *** <details> <summary>Internal Changes</summary> ##### Fixed - **ci**: Align pr.yml E2E with --dist loadscope ([#​1206](homeassistant-ai/ha-mcp#1206)) ([#​1247](homeassistant-ai/ha-mcp#1247)) - **ci**: Switch Renovate to a GitHub App token to allow workflow-file pushes ([#​1229](homeassistant-ai/ha-mcp#1229)) - **ci**: Break gemini-triage retrigger loop and bump turn budget ([#​1131](homeassistant-ai/ha-mcp#1131)) - **ci**: Harden gemini-triage so failures stop spamming user issues ([#​1122](homeassistant-ai/ha-mcp#1122)) - **ci**: Unbreak hotfix-release semantic-release run ([#​1091](homeassistant-ai/ha-mcp#1091)) ##### Chores - **addon**: Publish dev addon version 7.4.1.dev299 \[skip ci] ([`397aa6d`](homeassistant-ai/ha-mcp@397aa6d)) - **addon**: Publish dev addon version 7.4.1.dev298 \[skip ci] ([`942b7e0`](homeassistant-ai/ha-mcp@942b7e0)) - Sync tool docs after merge \[skip ci] ([`6823c47`](homeassistant-ai/ha-mcp@6823c47)) - **addon**: Publish dev addon version 7.4.1.dev297 \[skip ci] ([`6eac062`](homeassistant-ai/ha-mcp@6eac062)) - **addon**: Publish dev addon version 7.4.1.dev296 \[skip ci] ([`b2afe93`](homeassistant-ai/ha-mcp@b2afe93)) - **addon**: Publish dev addon version 7.4.1.dev295 \[skip ci] ([`4f4c4f3`](homeassistant-ai/ha-mcp@4f4c4f3)) - **deps**: Update ghcr.io/home-assistant/home-assistant docker tag to v2026.5.1 ([#​1236](homeassistant-ai/ha-mcp#1236)) - **addon**: Publish dev addon version 7.4.1.dev294 \[skip ci] ([`fd24991`](homeassistant-ai/ha-mcp@fd24991)) - **deps**: Update ghcr.io/astral-sh/uv docker tag to v0.11.13 ([#​1233](homeassistant-ai/ha-mcp#1233)) - **addon**: Publish dev addon version 7.4.1.dev293 \[skip ci] ([`fcc6496`](homeassistant-ai/ha-mcp@fcc6496)) - **addon**: Publish dev addon version 7.4.1.dev292 \[skip ci] ([`2961650`](homeassistant-ai/ha-mcp@2961650)) - **addon**: Publish dev addon version 7.4.1.dev291 \[skip ci] ([`5703112`](homeassistant-ai/ha-mcp@5703112)) - **addon**: Publish dev addon version 7.4.1.dev290 \[skip ci] ([`19b2f65`](homeassistant-ai/ha-mcp@19b2f65)) - **addon**: Publish dev addon version 7.4.1.dev289 \[skip ci] ([`e5a1365`](homeassistant-ai/ha-mcp@e5a1365)) - Sync tool docs after merge \[skip ci] ([`d2ff93b`](homeassistant-ai/ha-mcp@d2ff93b)) - **addon**: Publish dev addon version 7.4.1.dev288 \[skip ci] ([`0f62400`](homeassistant-ai/ha-mcp@0f62400)) - Sync tool docs after merge \[skip ci] ([`c7e2066`](homeassistant-ai/ha-mcp@c7e2066)) - **addon**: Publish dev addon version 7.4.1.dev287 \[skip ci] ([`c1133d4`](homeassistant-ai/ha-mcp@c1133d4)) - **addon**: Publish dev addon version 7.4.1.dev286 \[skip ci] ([`1ae790e`](homeassistant-ai/ha-mcp@1ae790e)) - **addon**: Publish dev addon version 7.4.1.dev285 \[skip ci] ([`2387d0c`](homeassistant-ai/ha-mcp@2387d0c)) - **addon**: Publish dev addon version 7.4.1.dev284 \[skip ci] ([`dd3a4a5`](homeassistant-ai/ha-mcp@dd3a4a5)) - **addon**: Publish dev addon version 7.4.1.dev283 \[skip ci] ([`78af8eb`](homeassistant-ai/ha-mcp@78af8eb)) - Sync tool docs after merge \[skip ci] ([`093fd74`](homeassistant-ai/ha-mcp@093fd74)) - **addon**: Publish dev addon version 7.4.1.dev282 \[skip ci] ([`2141e15`](homeassistant-ai/ha-mcp@2141e15)) - Sync tool docs after merge \[skip ci] ([`7810c95`](homeassistant-ai/ha-mcp@7810c95)) - **addon**: Publish dev addon version 7.4.1.dev281 \[skip ci] ([`7d79ec2`](homeassistant-ai/ha-mcp@7d79ec2)) - Sync tool docs after merge \[skip ci] ([`a73dc81`](homeassistant-ai/ha-mcp@a73dc81)) - **addon**: Publish dev addon version 7.4.1.dev280 \[skip ci] ([`c858ce3`](homeassistant-ai/ha-mcp@c858ce3)) - Sync tool docs after merge \[skip ci] ([`a587be0`](homeassistant-ai/ha-mcp@a587be0)) - **addon**: Publish dev addon version 7.4.1.dev279 \[skip ci] ([`b78ddb2`](homeassistant-ai/ha-mcp@b78ddb2)) - Sync tool docs after merge \[skip ci] ([`1210725`](homeassistant-ai/ha-mcp@1210725)) - **addon**: Publish dev addon version 7.4.1.dev278 \[skip ci] ([`a282c17`](homeassistant-ai/ha-mcp@a282c17)) - **addon**: Publish dev addon version 7.4.1.dev277 \[skip ci] ([`1081768`](homeassistant-ai/ha-mcp@1081768)) - Sync tool docs after merge \[skip ci] ([`e03f5d2`](homeassistant-ai/ha-mcp@e03f5d2)) - **addon**: Publish dev addon version 7.4.1.dev276 \[skip ci] ([`c4ef680`](homeassistant-ai/ha-mcp@c4ef680)) - **addon**: Publish dev addon version 7.4.1.dev275 \[skip ci] ([`780422d`](homeassistant-ai/ha-mcp@780422d)) - Sync tool docs after merge \[skip ci] ([`8a2bd1a`](homeassistant-ai/ha-mcp@8a2bd1a)) - **addon**: Publish dev addon version 7.4.1.dev274 \[skip ci] ([`f0f09de`](homeassistant-ai/ha-mcp@f0f09de)) - **addon**: Publish dev addon version 7.4.1.dev273 \[skip ci] ([`cb49f68`](homeassistant-ai/ha-mcp@cb49f68)) - **addon**: Publish dev addon version 7.4.1.dev272 \[skip ci] ([`5097186`](homeassistant-ai/ha-mcp@5097186)) - **addon**: Publish dev addon version 7.4.1.dev271 \[skip ci] ([`4714342`](homeassistant-ai/ha-mcp@4714342)) - **addon**: Publish dev addon version 7.4.1.dev270 \[skip ci] ([`217982a`](homeassistant-ai/ha-mcp@217982a)) - **addon**: Publish dev addon version 7.4.1.dev269 \[skip ci] ([`a65dd5f`](homeassistant-ai/ha-mcp@a65dd5f)) - Sync tool docs after merge \[skip ci] ([`0e6b54f`](homeassistant-ai/ha-mcp@0e6b54f)) - **addon**: Publish dev addon version 7.4.1.dev268 \[skip ci] ([`60ba1f2`](homeassistant-ai/ha-mcp@60ba1f2)) - **addon**: Publish dev addon version 7.4.1.dev267 \[skip ci] ([`13412aa`](homeassistant-ai/ha-mcp@13412aa)) - Sync tool docs after merge \[skip ci] ([`2702a0f`](homeassistant-ai/ha-mcp@2702a0f)) - **addon**: Publish dev addon version 7.4.1.dev266 \[skip ci] ([`77abe0b`](homeassistant-ai/ha-mcp@77abe0b)) - **addon**: Publish dev addon version 7.4.1.dev265 \[skip ci] ([`08b69db`](homeassistant-ai/ha-mcp@08b69db)) - Sync tool docs after merge \[skip ci] ([`c1f24b5`](homeassistant-ai/ha-mcp@c1f24b5)) - **addon**: Publish dev addon version 7.4.1.dev264 \[skip ci] ([`f2583f6`](homeassistant-ai/ha-mcp@f2583f6)) - Sync tool docs after merge \[skip ci] ([`c2ed2d3`](homeassistant-ai/ha-mcp@c2ed2d3)) - **addon**: Publish dev addon version 7.4.1.dev263 \[skip ci] ([`9d43e54`](homeassistant-ai/ha-mcp@9d43e54)) - **addon**: Publish dev addon version 7.4.1.dev262 \[skip ci] ([`a7355c8`](homeassistant-ai/ha-mcp@a7355c8)) - Sync tool docs after merge \[skip ci] ([`085bd8a`](homeassistant-ai/ha-mcp@085bd8a)) - Convert agents to skills ([#​1084](homeassistant-ai/ha-mcp#1084)) - **addon**: Publish dev addon version 7.4.1.dev261 \[skip ci] ([`0d1af36`](homeassistant-ai/ha-mcp@0d1af36)) - **addon**: Publish dev addon version 7.4.1.dev260 \[skip ci] ([`29397dc`](homeassistant-ai/ha-mcp@29397dc)) - **addon**: Publish dev addon version 7.4.1.dev259 \[skip ci] ([`4bbc74b`](homeassistant-ai/ha-mcp@4bbc74b)) - Sync tool docs after merge \[skip ci] ([`0f6d41e`](homeassistant-ai/ha-mcp@0f6d41e)) - **addon**: Publish dev addon version 7.4.1.dev258 \[skip ci] ([`6751d08`](homeassistant-ai/ha-mcp@6751d08)) - **addon**: Publish dev addon version 7.4.1.dev257 \[skip ci] ([`2213c89`](homeassistant-ai/ha-mcp@2213c89)) - **addon**: Publish dev addon version 7.4.1.dev256 \[skip ci] ([`18a366e`](homeassistant-ai/ha-mcp@18a366e)) - **addon**: Publish dev addon version 7.4.1.dev255 \[skip ci] ([`0e9b18d`](homeassistant-ai/ha-mcp@0e9b18d)) - **addon**: Publish dev addon version 7.4.1.dev254 \[skip ci] ([`39fc65b`](homeassistant-ai/ha-mcp@39fc65b)) - Sync tool docs after merge \[skip ci] ([`9fa0aea`](homeassistant-ai/ha-mcp@9fa0aea)) - **addon**: Publish dev addon version 7.4.1.dev253 \[skip ci] ([`0dcc59e`](homeassistant-ai/ha-mcp@0dcc59e)) - Sync tool docs after merge \[skip ci] ([`ec7413f`](homeassistant-ai/ha-mcp@ec7413f)) - **addon**: Publish dev addon version 7.4.1.dev252 \[skip ci] ([`345640c`](homeassistant-ai/ha-mcp@345640c)) - **addon**: Publish dev addon version 7.4.1.dev251 \[skip ci] ([`bab9d49`](homeassistant-ai/ha-mcp@bab9d49)) - Sync tool docs after merge \[skip ci] ([`726f0a5`](homeassistant-ai/ha-mcp@726f0a5)) - **addon**: Publish dev addon version 7.4.1.dev250 \[skip ci] ([`ded04ea`](homeassistant-ai/ha-mcp@ded04ea)) - **addon**: Publish dev addon version 7.4.1.dev249 \[skip ci] ([`37d5628`](homeassistant-ai/ha-mcp@37d5628)) - **addon**: Publish dev addon version 7.4.1.dev248 \[skip ci] ([`530786a`](homeassistant-ai/ha-mcp@530786a)) - Sync tool docs after merge \[skip ci] ([`36719c3`](homeassistant-ai/ha-mcp@36719c3)) - **addon**: Publish dev addon version 7.4.1.dev247 \[skip ci] ([`4dc47b5`](homeassistant-ai/ha-mcp@4dc47b5)) - **addon**: Publish dev addon version 7.4.1.dev246 \[skip ci] ([`6ffbd6a`](homeassistant-ai/ha-mcp@6ffbd6a)) - Sync tool docs after merge \[skip ci] ([`add66e3`](homeassistant-ai/ha-mcp@add66e3)) - **addon**: Publish dev addon version 7.4.1.dev245 \[skip ci] ([`d0114af`](homeassistant-ai/ha-mcp@d0114af)) - Sync tool docs after merge \[skip ci] ([`0ca41af`](homeassistant-ai/ha-mcp@0ca41af)) - **addon**: Publish dev addon version 7.4.1.dev244 \[skip ci] ([`d052dd0`](homeassistant-ai/ha-mcp@d052dd0)) - **addon**: Publish dev addon version 7.4.0.dev243 \[skip ci] ([`827bc65`](homeassistant-ai/ha-mcp@827bc65)) - Bump package version to 7.4.1 to match released addon ([`4f65497`](homeassistant-ai/ha-mcp@4f65497)) - **addon**: Publish dev addon version 7.4.0.dev242 \[skip ci] ([`8ba80ae`](homeassistant-ai/ha-mcp@8ba80ae)) - **addon**: Publish hotfix version 7.4.1 ([`bda75e6`](homeassistant-ai/ha-mcp@bda75e6)) - **addon**: Publish dev addon version 7.4.0.dev241 \[skip ci] ([`2126428`](homeassistant-ai/ha-mcp@2126428)) ##### Continuous Integration - **deps**: Bump renovatebot/github-action in the github-actions group ([#​1218](homeassistant-ai/ha-mcp#1218)) - **deps**: Bump renovatebot/github-action in the github-actions group ([#​1111](homeassistant-ai/ha-mcp#1111)) ##### Refactoring - Extract \_fetch\_dashboards\_list helper ([#​1193](homeassistant-ai/ha-mcp#1193)) ([#​1207](homeassistant-ai/ha-mcp#1207)) ##### Testing - **e2e**: Module-scope bulk\_automations + bulk\_scripts fixtures (refs [#​366](homeassistant-ai/ha-mcp#366)) ([#​1275](homeassistant-ai/ha-mcp#1275)) - **e2e**: Lower INPUT\_BOOLEAN\_WAIT from 30s to 10s (refs [#​366](homeassistant-ai/ha-mcp#366)) ([#​1273](homeassistant-ai/ha-mcp#1273)) - **e2e**: Generalize readiness-gate diagnostics helper (closes [#​1267](homeassistant-ai/ha-mcp#1267)) ([#​1271](homeassistant-ai/ha-mcp#1271)) - **e2e**: Narrow except clauses in e2e polling helpers (closes [#​1266](homeassistant-ai/ha-mcp#1266)) ([#​1270](homeassistant-ai/ha-mcp#1270)) - **e2e**: Drop ha\_mcp\_tools retry-path + pre-install manifest requirements ([#​1268](homeassistant-ai/ha-mcp#1268)) - **e2e**: Instrument and retry ha\_mcp\_tools readiness wait ([#​1262](homeassistant-ai/ha-mcp#1262)) - Use time.monotonic() in UAT runner and test\_env\_manager ([#​1254](homeassistant-ai/ha-mcp#1254)) - **e2e**: Detect partial/corrupt hacs\_frontend dir in fast-path guard ([#​1253](homeassistant-ai/ha-mcp#1253)) - **e2e**: Remove unused wait/assert helpers ([post-#​1249](https://github.com/post-/ha-mcp/issues/1249) audit) ([#​1256](homeassistant-ai/ha-mcp#1256)) - **e2e**: Clear stale .hacs\_frontend.lock from prior crashed runs ([#​1252](homeassistant-ai/ha-mcp#1252)) - **e2e**: Use time.monotonic() in workflow polling loops ([#​1258](homeassistant-ai/ha-mcp#1258)) - **e2e**: Use time.monotonic() for duration polling ([#​1234](homeassistant-ai/ha-mcp#1234)) ([#​1249](homeassistant-ai/ha-mcp#1249)) - **e2e**: Close ARM ha\_mcp\_tools readiness race under loadscope ([#​1208](homeassistant-ai/ha-mcp#1208)) - **hacs**: Tighten is\_hacs\_unavailable to not match legitimate "Repository not found" ([#​1246](homeassistant-ai/ha-mcp#1246)) - **seed**: Unblock 3 silent-skip pagination/state tests via baked recorder DB ([#​1240](homeassistant-ai/ha-mcp#1240)) - **seed**: Register a writable local\_calendar to unblock event-creation test ([#​1243](homeassistant-ai/ha-mcp#1243)) - **addon**: Fix base64 padding-bit flake in token tamper tests ([#​1238](homeassistant-ai/ha-mcp#1238)) ([#​1241](homeassistant-ai/ha-mcp#1241)) - **seed**: Add a writable scene for test\_call\_service\_scene\_turn\_on ([#​1231](homeassistant-ai/ha-mcp#1231)) - **seed**: Assign demo device to living\_room area for filter test ([#​1230](homeassistant-ai/ha-mcp#1230)) - **e2e**: Drop nonexistent sun service from session readiness wait ([#​1227](homeassistant-ai/ha-mcp#1227)) - **e2e**: Self-contain dashboard register/remove to fix ARM xdist race ([#​1196](homeassistant-ai/ha-mcp#1196)) ([#​1201](homeassistant-ai/ha-mcp#1201)) - Fix EN dash in docstring causing RUF002 lint failure ([`eac5916`](homeassistant-ai/ha-mcp@eac5916)) - Address Gemini review feedback on host detection and port allocation ([`960305e`](homeassistant-ai/ha-mcp@960305e)) - Fix three categories of E2E test flakiness ([`39417ff`](homeassistant-ai/ha-mcp@39417ff)) - **e2e**: Pin config\_hash stability for dashboards ([#​1132](homeassistant-ai/ha-mcp#1132)) </details> </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNyIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19--> Co-authored-by: todd <tpunderson@greyrock.io> Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/26
… / Settings UI (#1486) * fix(addon): enable ingress so the stable add-on shows the Open Web UI / 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 (#960) and #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> * test(addon): assert ingress_stream in the stable add-on config 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> * docs(addon): document the stable web Settings UI and clarify config sync - 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> --------- Co-authored-by: kingpanther13 <kingpanther13@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What does this PR do?
Adds a self-contained web settings page for configuring per-tool enable/disable/pin states. Served directly by the FastMCP HTTP server, works across all installation methods (add-on, Docker, standalone).
Context: Discussed with @julienld in #857 — he suggested using the built-in FastMCP HTTP server to expose a configuration UI that works across all installation methods rather than relying on HAOS add-on config.yaml schema. This PR implements that approach.
UI features
enabledandpinnedswitches. Pinned is grayed out when a tool is disabled.ha_config_set_yaml, the four filesystem tools,ha_install_mcp_tools) appear in the list with a stub note pointing atdocs/beta.mdwhether or not they are currently registered. Their toggles are locked because they require the underlying feature flag.ha_search_entities,ha_get_overview,ha_get_state,ha_report_issueshown withmandatorybadge and locked togglesPOST /addons/self/restart) to apply changes with one clickEndpoints and where they mount
The settings UI uses FastMCP custom routes, which bypass FastMCP's auth middleware. To match the auth-by-obscurity model the rest of the server uses for HTTP modes, routes are mounted under the MCP secret path (
/<secret_path>/...) so HTTP clients need the same secret to reach the UI as they do to reach the MCP endpoint itself. In add-on mode the routes are also mounted at root so HA ingress (which proxieslocalhost:9583/) can serve the "Open Web UI" button./and/settings/<secret_path>/settings/api/settings/tools(GET / POST)/<secret_path>/api/settings/tools(GET / POST)/api/settings/restart(POST)SUPERVISOR_TOKEN)/api/settings/info(GET)If neither
SUPERVISOR_TOKENnor a secret path is available,register_settings_routeslogs a warning and registers nothing rather than expose the routes publicly.Persistence and apply model
/data/tool_config.json(add-on) or~/.ha-mcp/tool_config.json(standalone) on every toggle. The path is selected bySUPERVISOR_TOKENpresence, not by/datadirectory existence (matches the rest of the file).mcp.disable()calls accumulate transforms unreliably; startup-time apply via_apply_settings_visibility()is the clean path. Disabled tools are fully removed from the MCP tool list on next startup.DISABLED_TOOLS/PINNED_TOOLSenv vars on first run (backwards-compatible with existing env var config).enable_yaml_config_editing,enable_tool_search,enable_skills, etc.) remain in the dev add-on config page as source of truth. AND-semantics: a beta-gated tool is enabled only when the safety toggle is on and the UI hasn't disabled it.Beta tool integration (post-#942)
After #942 and #1030 formalized the beta-channel system, beta-tagged tools (
ha_config_set_yaml,ha_list_files,ha_read_file,ha_write_file,ha_delete_file,ha_install_mcp_tools) only register when their feature flag is set. The settings UI injects stub entries for the unregistered ones so users can see the tool exists and how to enable it; the stub points atdocs/beta.mdwhich covers both the dev-channel toggle and the env-var path.Add-on integration
ingress: true+ingress_port: 9583+ingress_stream: trueonhomeassistant-addon-dev/config.yaml— shows the "Open Web UI" button on the add-on info pagedisabled_toolsandpinned_toolstext fields (seed/fallback for users who don't want the web UI) plustool_search_max_results(range-validated 2-10 in both Pydantic and the addon schema)homeassistant-addon/(stable channel)config.yamlis intentionally untouched — these are dev-only options for now, matching the existing convention for beta features.homeassistant-addon/start.pyis modified because the dev add-on shares it viaCOPYinhomeassistant-addon-dev/Dockerfile; it reads the new options with safe defaults so the stable add-on keeps working unchanged.Tool search max results wired through
tool_search_max_results(2-10, default 5) now actually flows intoCategorizedSearchTransform(max_results=...)instead of the previous hardcoded5. Pydantic enforces the range; the dev addon schema usesint(2,10)?so the supervisor UI rejects out-of-range values before they reach env vars.Closes #798
Type of change
Testing
uv run pytest)uv run ruff check)Checklist